/******************************************************************************
 * Name         : useopt.c
 * Title        : USE Optimiser
 * Author       : Matthew Wahab
 * Created      : Jan 2008
 *
 * Copyright    : 2002-2008 by Imagination Technologies Limited.
 *              : All rights reserved. No part of this software, either
 *              : material or conceptual may be copied or distributed,
 *              : transmitted, transcribed, stored in a retrieval system or
 *              : translated into any human or computer language in any form
 *              : by any means, electronic, mechanical, manual or otherwise,
 *              : or disclosed to third parties without the express written
 *              : permission of Imagination Technologies Limited, Home Park
 *              : Estate, Kings Langley, Hertfordshire, WD4 8LZ, U.K.
 * Modifications:-
 * $Log: useopt.c $
 ******************************************************************************/

#include "useopt.h"
#include "osglue.h"

/*
 * Macro definitions
 */

/* Size macros */
#define UINTS_TO_SPAN_BITS(B)	(((B) + 31) >> 5)

/* USE_REGTYPE_UNDEF: Undefined register type */
#define USE_REGTYPE_UNDEF USEASM_REGTYPE_MAXIMUM

/* USE_OPCODE_UNDEF: Undefined opcode */
#define USE_OPCODE_UNDEF USEASM_OP_MAXIMUM


/* Utility macro for free data */
#define UseoptFree(s,x) { _UseoptFree(s,x); x = NULL; }

/* Utility macro for enforcing requirements */
#define REQUIRE(s,e) { UseoptRequire(s,e, __FILE__, __LINE__);}

/* Utility macro for enforcing tests */
#define ENSURE(s,c) { if (!(c)) UseoptRequire(s,USEOPT_FAILED, __FILE__, __LINE__); }

/* Set status number */
#if defined(DEBUG)
#define SET_STATUS(s,n) { psState->eStatus = n; psState->pcFile = __FILE__; psState->uLine = __LINE__; }
#else /* !defined(DEBUG) */
#define SET_STATUS(s,n) { psState->eStatus = n; }
#endif /* defined(DEBUG) */

/*
 * Data types
 */

/* Channel names and numbers */
typedef enum _USC_REG_CHANS_
{
	USE_BLUE_CHAN = 2,
	USE_GREEN_CHAN = 1,
	USE_RED_CHAN = 0,
	USE_ALPHA_CHAN = 3,

	USE_MAX_REG_CHAN
} USE_REG_CHANS;

/* Generic channel masks */
#define USE_RGB_CHAN_MASK ((1 << USE_RED_CHAN) |	\
						   (1<< USE_GREEN_CHAN) |	\
						   (1 << USE_BLUE_CHAN))
#define USE_ALPHA_CHAN_MASK (1 << USE_ALPHA_CHAN)
#define USE_ALL_CHAN_MASK ((1 << USE_ALPHA_CHAN) | USE_RGB_CHAN_MASK)

/* Instruction Flags */
typedef enum _USE_INST_FLAGS_
{
	USE_SKIPINV,
	USE_NOSCHED,
	USE_PROGEND,
	USE_SATURATE,

	USE_INST_FLAGS_MAX
} USE_INST_FLAGS;

/* INST_LIST: List of instructions treated as the interval [psFirst, psLast] */
typedef struct _INST_LIST_
{
	PUSE_INST psFirst;
	PUSE_INST psLast;
} INST_LIST, *PINST_LIST;
#define INST_LIST_DEFAULT { NULL, NULL }

/* USE_BLOCK: Code block */
typedef struct _USE_BLOCK_
{
	PUSE_INST psFirst;    // First instruction in block
	PUSE_INST psLast;     // Last instruction in block

	struct _USE_BLOCK_* psPrev;    // Previous block
	struct _USE_BLOCK_* psNext;    // Next block
} USE_BLOCK, *PUSE_BLOCK;
#define USE_BLOCK_DEFAULT { NULL, NULL, NULL, NULL }

/*
  BLOCK_STACK: Stack of instruction lists
*/
typedef struct _BLOCK_STACK_
{
	PUSE_BLOCK psBlock;
	struct _BLOCK_STACK_* psNext;
} BLOCK_STACK, *PBLOCK_STACK;

typedef enum _OPT_FLAGS_
{
	OPTFLAG_IGNORE_OUTREG = 0,      // Used by GetNodeRepl

	OPTFLAG_MAXIMUM
} OPT_FLAGS;

/* NODE_REPL: Replacement data for a node */
typedef struct _NODE_REPL_
{
	USE_REGISTER sReg;
	IMG_UINT32 uInstFlags;
} NODE_REPL, *PNODE_REPL;

/*
  USEOPT_STATE: Optimiser state
 */
typedef struct _USEOPT_STATE_
{
	USEOPT_STATUS eStatus;
	SGX_CORE_INFO* psTarget;

	PUSEASM_CONTEXT psContext;
	PUSEOPT_DATA psOptData;

	/* Error handler */
	jmp_buf sErrorHandler;
	IMG_BOOL bErrorHandlerValid;
#ifdef DEBUG
	const char* pcFile;          // File causing failure.
	IMG_UINT32 uLine;      // Line number of failure.
#endif

	/* Register information */
	IMG_UINT32 uNumTemps;
	IMG_UINT32 uNumPrimAttrs;
	IMG_UINT32 uNumOutputs;

	IMG_UINT32 uNumNodes;
	/*
	 * asNodeRepl: Replacements for nodes (generated by eliminating mov instructions).
	 */
	PNODE_REPL asNodeRepl;

	/* Optimiser flags */
	IMG_UINT32 auFlags [1];

	/* Bitvector of output nodes */
	IMG_UINT32* auOutputNode;

	/* Program start */
	PUSE_INST psProgramStart;

} USEOPT_STATE, *PUSEOPT_STATE;

/*****************************************************************************
 FUNCTION   : UseoptInitReg

 PURPOSE    : Initialise a useasm register

 PARAMETERS : psReg    - Register to initialise

 RETURNS    : Nothing
*****************************************************************************/
static
IMG_VOID UseoptInitReg(PUSE_REGISTER psReg)
{
	UseAsmInitReg(psReg);
	psReg->uType = USEASM_REGTYPE_UNDEF;
}

/*****************************************************************************
 FUNCTION   : UseoptBitVecSize

 PURPOSE    : Calculate the number of bytes needed for a bitvector

 PARAMETERS : uNumBits    - Number of bits in bitvector to get

 RETURNS    : Number of bytes needed for the bitvector
*****************************************************************************/
IMG_INTERNAL
IMG_UINT32 UseoptBitVecSize(const IMG_UINT32 uNumBits)
{
	return (sizeof(IMG_UINT32) * UINTS_TO_SPAN_BITS(uNumBits));
}

/*****************************************************************************
 FUNCTION   : UseoptGetBit

 PURPOSE    : Get a bit in a bitvector represented as an array of integers

 PARAMETERS : auArr    - Bitvector
              uBit     - Bit to get

 RETURNS    : IMG_TRUE if the bit is set, IMG_FALSE otherwise
*****************************************************************************/
IMG_INTERNAL
IMG_BOOL UseoptGetBit(const IMG_UINT32 auBitVec[],
				const IMG_UINT32 uBit)
{
	return auBitVec[uBit >> 5] & (1 << (uBit % 32)) ? IMG_TRUE : IMG_FALSE;
}

/*****************************************************************************
 FUNCTION   : UseoptSetBit

 PURPOSE    : Set a bit in a bitvector represented as an array of integers

 PARAMETERS : auArr    - Bitvector
              uBit     - Bit to set
              bBitData - IMG_TRUE: set the bit, IMG_FALSE: clear the bit

 RETURNS    : Nothing
*****************************************************************************/
IMG_INTERNAL
IMG_VOID UseoptSetBit(IMG_UINT32 auBitVec[],
					  IMG_UINT32 uBit,
					  IMG_BOOL bBitValue)
{
	if (bBitValue)
	{
		auBitVec[uBit >> 5] |= (1 << (uBit % 32));
	}
	else
	{
		auBitVec[uBit >> 5] &= ~(1 << (uBit % 32));
	}
}

/*****************************************************************************
 FUNCTION   : UseoptAbort

 PURPOSE    : Error handling

 PARAMETERS : psState    - Optimiser state
              eError     - Error number

 RETURNS    : Nothing
*****************************************************************************/
static
IMG_VOID UseoptAbort(PUSEOPT_STATE psState,
					 USEOPT_STATUS eError)
{
	if (psState == NULL)
	{
		/* No state so fail immediately */
		abort();
	}

	/* Jump to error handler */
	if (!psState->bErrorHandlerValid)
	{
		abort();
	}
	SET_STATUS(psState, eError);
	longjmp(psState->sErrorHandler, IMG_TRUE);
}

/*****************************************************************************
 FUNCTION   : UseoptRequire

 PURPOSE    : Test for a condition, failing if it is not met.

 PARAMETERS : psState    - Optimiser state
              eError     - Error number

 RETURNS    : Nothing
*****************************************************************************/
static
IMG_VOID UseoptRequire(PUSEOPT_STATE psState,
					   USEOPT_STATUS eError,
					   const char* pcFile,
					   IMG_UINT32 uLine)
{
	PVR_UNREFERENCED_PARAMETER(pcFile);
	PVR_UNREFERENCED_PARAMETER(uLine);
#ifdef DEBUG
	psState->pcFile = pcFile;
	psState->uLine = uLine;
#endif
	UseoptAbort(psState, eError);
}


/*****************************************************************************
 FUNCTION   : UseoptAlloc

 PURPOSE    : Allocate memory

 PARAMETERS : psState    - Optimiser state
              uSize      - Size in bytes of memory to allocate

 RETURNS    : Newly allocated memory or NULL
*****************************************************************************/
static
IMG_PVOID UseoptAlloc(PUSEOPT_STATE psState,
					  IMG_UINT32 uSize)
{
    IMG_PVOID pvRet = NULL;

    if (psState->psOptData != NULL)
	{
		pvRet = psState->psOptData->pfnAlloc(psState->psContext, uSize);
	}
	if (pvRet == NULL)
	{
		SET_STATUS(psState, USEOPT_MEMALLOC);
	}

    return pvRet;
}

/*****************************************************************************
 FUNCTION   : UseoptFree

 PURPOSE    : Free memory

 PARAMETERS : psState    - Optimiser state
              pvPtr      - Pointer to block to free

 RETURNS    : Nothing
*****************************************************************************/
static
IMG_VOID _UseoptFree(PUSEOPT_STATE psState,
					 IMG_PVOID pvPtr)
{
    if (pvPtr == NULL)
		return;
    if (psState->psOptData == NULL)
        return;

    psState->psOptData->pfnFree(psState->psContext, pvPtr);
}

/*****************************************************************************
 FUNCTION	: IsNode

 PURPOSE	: Test whether a register is a node for the optimiser

 PARAMETERS	: psState    - Optimiser state
              psReg      - Register to test

 RETURNS	: IMG_TRUE iff register is a node
*****************************************************************************/
static
IMG_BOOL IsNode(PUSEOPT_STATE psState,
				PUSE_REGISTER psReg)
{
	USEASM_REGTYPE eRegType;
	IMG_UINT32 uRegNum;

	if (psReg == NULL)
		return IMG_FALSE;
	eRegType = psReg->uType;
	uRegNum = psReg->uNumber;

	if (!(eRegType < USEASM_REGTYPE_MAXIMUM))
	{
		return IMG_FALSE;
	}

	if (psReg->uIndex != USEREG_INDEX_NONE)
	{
		return IMG_FALSE;
	}

	if (eRegType == USEASM_REGTYPE_TEMP)
	{
		const IMG_PUINT32 auKeepTemp = psState->psOptData->auKeepTempReg;

 		if (uRegNum < psState->uNumTemps)
		{
			if (auKeepTemp == NULL)
			{
				return IMG_TRUE;
			}
			else if (!UseoptGetBit(auKeepTemp, uRegNum))
			{
				return IMG_TRUE;
			}
			else
			{
				return IMG_FALSE;
			}
		}
	}
	if (eRegType == USEASM_REGTYPE_PRIMATTR)
	{
		const IMG_PUINT32 auKeepPA = psState->psOptData->auKeepPAReg;

		if (uRegNum < psState->uNumPrimAttrs)
		{
			if (auKeepPA == NULL)
			{
				return IMG_TRUE;
			}
			else if (!UseoptGetBit(auKeepPA, uRegNum))
			{
				return IMG_TRUE;
			}
			else
			{
				return IMG_FALSE;
			}
		}
	}
	if (eRegType == USEASM_REGTYPE_OUTPUT)
	{
		const IMG_PUINT32 auKeepOutput = psState->psOptData->auKeepOutputReg;

		if (uRegNum < psState->uNumOutputs)
		{
			if (auKeepOutput == NULL)
			{
				return IMG_TRUE;
			}
			else if (!UseoptGetBit(auKeepOutput, uRegNum))
			{
				return IMG_TRUE;
			}
			else
			{
				return IMG_FALSE;
			}
		}
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: RegToNode

 PURPOSE	: Get the node number of a register

 PARAMETERS	: psState    - Optimiser state
              psReg      - Register to convert

 RETURNS	: Node number of register or USE_REGTYPE_UNDEF
*****************************************************************************/
static
IMG_UINT32 RegToNode(PUSEOPT_STATE psState,
					 PUSE_REGISTER psReg)
{
	const USEASM_REGTYPE eRegType = psReg->uType;
	const IMG_UINT32 uRegNum = psReg->uNumber;

	if (eRegType == USEASM_REGTYPE_TEMP)
	{
		return uRegNum;
	}
	if (eRegType == USEASM_REGTYPE_PRIMATTR)
	{
		return (uRegNum + psState->uNumTemps);
	}
	if (eRegType == USEASM_REGTYPE_OUTPUT &&
		uRegNum < psState->uNumOutputs)
	{
		return (uRegNum +
				psState->uNumTemps +
				psState->uNumPrimAttrs);
	}
	return USE_REGTYPE_UNDEF;
}

/*****************************************************************************
 FUNCTION	: NodeToReg

 PURPOSE	: Get the register type and number of a node

 PARAMETERS	: psState    - Optimiser state
              uNode      - Node to convert

 OUTPUT     : puType     - Register type
              puNumber   - Register number

 RETURNS	: Node number of register or USE_REGTYPE_UNDEF
*****************************************************************************/
static
IMG_VOID NodeToReg(PUSEOPT_STATE psState,
				   IMG_UINT32 uNode,
				   USEASM_REGTYPE* puType,
				   IMG_PUINT32 puNumber)
{
	USEASM_REGTYPE uType = USE_REGTYPE_UNDEF;
	IMG_UINT32 uNumber =  USE_UNDEF;

	if (uNode < psState->uNumTemps)
	{
		uType = USEASM_REGTYPE_TEMP;
		uNumber = uNode;
	}
	else
	{
		uNode -= psState->uNumTemps;
		if (uNode < psState->uNumPrimAttrs)
		{
			uType = USEASM_REGTYPE_PRIMATTR;
			uNumber = uNode;
		}
		else
		{
			uNode -= psState->uNumPrimAttrs;
			if (uNode < psState->uNumOutputs)
			{
				uType = USEASM_REGTYPE_OUTPUT;
				uNumber = uNode;
			}
		}
	}

	if (puType != NULL)
		(*puType) = uType;
	if (puNumber != NULL)
		(*puNumber) = uNumber;
}

/*****************************************************************************
 FUNCTION   : FixUseoptData

 PURPOSE    : Clean up supplied optimiser data

 PARAMETERS : psUseoptData    - Optimiser data

 RETURNS    : Newly allocated and initialised state
*****************************************************************************/
static
IMG_VOID FixUseoptData(PUSEOPT_DATA psUseoptData)
{
	IMG_UINT32 uCtr;

	/* Make sure that register bounds are accurate */
	for (uCtr = 0; uCtr < psUseoptData->uNumOutRegs; uCtr++)
	{
		IMG_UINT32 uTmp;
		PUSE_REGISTER psCurr;
		psCurr = &psUseoptData->asOutRegs[uCtr];

		if (psCurr->uType == USEASM_REGTYPE_TEMP)
		{
			uTmp = psUseoptData->uNumTempRegs;
			if (psCurr->uNumber > uTmp && psUseoptData->auKeepTempReg == NULL)
			{
				psUseoptData->uNumTempRegs = psCurr->uNumber + 1;
			}
		}
		else if (psCurr->uType == USEASM_REGTYPE_PRIMATTR)
		{
			uTmp = psUseoptData->uNumPARegs;
			if (psCurr->uNumber > uTmp && psUseoptData->auKeepPAReg == NULL)
			{
				psUseoptData->uNumPARegs = psCurr->uNumber + 1;
			}
		}
		else if (psCurr->uType == USEASM_REGTYPE_OUTPUT)
		{
			uTmp = psUseoptData->uNumOutputRegs;
			if (psCurr->uNumber > uTmp && psUseoptData->auKeepOutputReg == NULL)
			{
				psUseoptData->uNumOutputRegs = psCurr->uNumber + 1;
			}
		}
	}
}

/*****************************************************************************
 FUNCTION	: FreeOptState

 PURPOSE	: Free the optimiser state

 PARAMETERS	: psState    - Optimiser state

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID FreeOptState(PUSEOPT_STATE psState)
{
	USEASM_FREEFN pfnFree;
	PUSEASM_CONTEXT psContext;

	if (psState == NULL)
		return;

	UseoptFree(psState, psState->asNodeRepl);
	UseoptFree(psState, psState->auOutputNode);

	pfnFree = psState->psOptData->pfnFree;
	psContext = psState->psContext;

	pfnFree(psContext, psState);
}

/*****************************************************************************
 FUNCTION   : InitOptState

 PURPOSE    : Initialise the optimiser state

 PARAMETERS : psTarget   - Target information
              psContext  - Assembler context

 RETURNS    : Newly allocated and initialised state
*****************************************************************************/
static
PUSEOPT_STATE InitOptState(PSGX_CORE_INFO psTarget,
                           PUSEASM_CONTEXT psContext,
                           PUSEOPT_DATA psOptData)
{
	IMG_BOOL bFailed = IMG_FALSE;
	PUSEOPT_STATE psState = NULL;

	/* Allocate the state */
	if (psOptData == NULL)
	    return NULL;
	if (psOptData->pfnAlloc == NULL)
	    return NULL;

	psState = psOptData->pfnAlloc(psContext, sizeof(USEOPT_STATE));
	if (psState == NULL)
	{
		return NULL;
	}

	/* Initialise the data */
	UseAsm_MemSet(psState, 0, sizeof(*psState));

	psState->eStatus = USEOPT_OK;
	psState->psContext = psContext;
	psState->psTarget = psTarget;
	psState->psOptData = psOptData;
	psState->bErrorHandlerValid = IMG_FALSE;

	if (psOptData != NULL)
	{
		IMG_UINT32 uNumNodes = 0;

		psState->uNumTemps = psOptData->uNumTempRegs;
		psState->uNumPrimAttrs = psOptData->uNumPARegs;
		psState->uNumOutputs = psOptData->uNumOutputRegs;

		uNumNodes = (psState->uNumTemps +
					 psState->uNumPrimAttrs +
					 psState->uNumOutputs);

		psState->uNumNodes = uNumNodes;
		if (uNumNodes > 0)
		{
			IMG_UINT32 uCtr;
			IMG_UINT32 uSize;

			uSize = (sizeof(NODE_REPL) * uNumNodes);
			psState->asNodeRepl = UseoptAlloc(psState, uSize);
			if (psState->asNodeRepl == NULL)
			{
				bFailed = IMG_TRUE;
				goto Exit;
			}

			for (uCtr = 0; uCtr < uNumNodes; uCtr++)
			{
				UseoptInitReg(&psState->asNodeRepl[uCtr].sReg);
				psState->asNodeRepl[uCtr].sReg.uType = USE_REGTYPE_UNDEF;
				psState->asNodeRepl[uCtr].uInstFlags = 0;
			}

			/* Record output nodes */
			uSize = sizeof(IMG_UINT32) * UINTS_TO_SPAN_BITS(uNumNodes);
			psState->auOutputNode = UseoptAlloc(psState, uSize);
            if (psState->auOutputNode == NULL)
			{
				bFailed = IMG_TRUE;
				goto Exit;
			}

			UseAsm_MemSet(psState->auOutputNode, 0, uSize);

			for (uCtr = 0;
				 uCtr < psState->psOptData->uNumOutRegs;
				 uCtr ++)
			{
				IMG_UINT32 uNode;
				PUSE_REGISTER psReg;

				psReg = &psState->psOptData->asOutRegs[uCtr];
				if (IsNode(psState, psReg))
				{
					uNode = RegToNode(psState, psReg);
					UseoptSetBit(psState->auOutputNode, uNode, IMG_TRUE);
				}
			}
		}
	}

  Exit:
	if (bFailed)
	{
		FreeOptState(psState);
		psState = NULL;
	}

	return psState;
}

/*****************************************************************************
 FUNCTION	: EqualRegs

 PURPOSE	: Test whether a two registers are the same

 PARAMETERS	: bSimple    - Do a simple test (type and number only)
              psReg1     - First register
              psReg2     - Second register

 RETURNS	: IMG_TRUE iff registers are the same
*****************************************************************************/
static
IMG_UINT32 EqualRegs(IMG_BOOL bSimple,
					 PUSE_REGISTER psReg1,
					 PUSE_REGISTER psReg2)
{
	if ((psReg1->uType != psReg2->uType) ||
		(psReg1->uNumber != psReg2->uNumber))
	{
		return IMG_FALSE;
	}
	if (bSimple)
		return IMG_TRUE;

	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: IsOutput

 PURPOSE	: Test whether a register is a program output

 PARAMETERS	: psState    - Optimiser state
              psReg      - Register to test

 RETURNS	: IMG_TRUE iff psReg is a program output
*****************************************************************************/
static
IMG_BOOL IsOutput(PUSEOPT_STATE psState,
				  PUSE_REGISTER psReg)

{
	IMG_UINT32 uCtr;

	if (IsNode(psState, psReg))
	{
		IMG_UINT32 uNode;

		uNode = RegToNode(psState, psReg);

		return UseoptGetBit(psState->auOutputNode, uNode);
	}

	for (uCtr = 0;
		 uCtr < psState->psOptData->uNumOutRegs;
		 uCtr ++)
	{
		if (EqualRegs(IMG_TRUE, psReg, &psState->psOptData->asOutRegs[uCtr]))
		{
			return IMG_TRUE;
		}
	}
	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: SetInstFlags

 PURPOSE	: Set instruction flags

 PARAMETERS	: psState    - Opimiser state
              psInst     - Instruction
              uFlags     - Flags to set
              bSet       - Set (IMG_TRUE) or clear (IMG_FALSE)

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID SetInstFlags(PUSE_INST psInst,
					  IMG_UINT32 uFlags,
					  IMG_BOOL bSet)
{
	if (uFlags & (1 << USE_PROGEND))
	{
		if (bSet)
			psInst->uFlags1 |= USEASM_OPFLAGS1_END;
		else
			psInst->uFlags1 &= ~USEASM_OPFLAGS1_END;
	}

	if (uFlags & (1 << USE_NOSCHED))
	{
		if (bSet)
			psInst->uFlags1 |= USEASM_OPFLAGS1_NOSCHED;
		else
			psInst->uFlags1 &= ~USEASM_OPFLAGS1_NOSCHED;
	}

	if (uFlags & (1 << USE_SKIPINV))
	{
		if (bSet)
			psInst->uFlags1 |= USEASM_OPFLAGS1_SKIPINVALID;
		else
			psInst->uFlags1 &= ~USEASM_OPFLAGS1_SKIPINVALID;
	}

	if (uFlags & (1 << USE_SATURATE))
	{
		if (bSet)
			psInst->uFlags2 |= USEASM_OPFLAGS2_SAT;
		else
			psInst->uFlags2 &= ~USEASM_OPFLAGS2_SAT;
	}
}

/*****************************************************************************
 FUNCTION	: GetInstFlags

 PURPOSE	: Get instruction flags

 PARAMETERS	: psState    - Opimiser state
              psInst     - Instruction

 RETURNS	: Known instruction flags
*****************************************************************************/
static
IMG_UINT32 GetInstFlags(PUSE_INST psInst)
{
	IMG_UINT32 uFlags = 0;

	if (psInst->uFlags1 & USEASM_OPFLAGS1_END)
    {
		uFlags |= (1 << USE_PROGEND);
    }

    if (psInst->uFlags1 & USEASM_OPFLAGS1_SKIPINVALID)
    {
		uFlags |= (1 << USE_SKIPINV);
    }

	if (psInst->uFlags1 & USEASM_OPFLAGS1_NOSCHED)
    {
		uFlags |= (1 << USE_NOSCHED);
    }

    if (psInst->uFlags2 & USEASM_OPFLAGS2_SAT)
    {
		uFlags |= (1 << USE_SATURATE);
    }

	return uFlags;
}

/*****************************************************************************
 FUNCTION	: GetPredicate

 PURPOSE	: Get the predicate for an instruction

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: Predicate of instruction
*****************************************************************************/
static
USEASM_PRED GetPredicate(PUSE_INST psInst)
{
	IMG_UINT32 uPred =  USE_UNDEF;

	uPred = psInst->uFlags1 & ~USEASM_OPFLAGS1_PRED_CLRMSK;
	uPred >>= USEASM_OPFLAGS1_PRED_SHIFT;

	return (USEASM_PRED)uPred;
}

/*****************************************************************************
 FUNCTION	: IsProgEnd

 PURPOSE	: Test whether an instruction has the program end flag set.

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff instruction has the program end flag set.
*****************************************************************************/
static
IMG_BOOL IsProgEnd(PUSE_INST psInst)
{
	if (psInst->uFlags1 & USEASM_OPFLAGS1_END)
		return IMG_TRUE;
	else
		return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: SetProgEnd

 PURPOSE	: Set the program end flag

 PARAMETERS	: psInst     - Instruction
              bSet       - Set or clear the program end flag

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID SetProgEnd(PUSE_INST psInst, IMG_BOOL bSet)
{
	if (bSet)
	{
		psInst->uFlags1 |= USEASM_OPFLAGS1_END;
	}
	else
	{
		psInst->uFlags1 &= ~USEASM_OPFLAGS1_END;
	}
}

/*****************************************************************************
 FUNCTION	: GetRepeatCount

 PURPOSE	: Get an instructions repeat count

 PARAMETERS	: psInst     - Instruction to query

 RETURNS	: Repeat count for the instruction
*****************************************************************************/
static
IMG_UINT32 GetRepeatCount(PUSE_INST psInst)
{
	IMG_UINT32 uCount =  USE_UNDEF;

	uCount = psInst->uFlags1 & ~USEASM_OPFLAGS1_REPEAT_CLRMSK;
	uCount >>= USEASM_OPFLAGS1_REPEAT_SHIFT;

	return uCount;
}

/*****************************************************************************
 FUNCTION	: SetRepeatCount

 PURPOSE	: Set an instructions repeat count

 PARAMETERS	: psInst     - Instruction
              uCount     - Repeat count to set

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID SetRepeatCount(PUSE_INST psInst, IMG_UINT32 uCount)
{
	IMG_UINT32 uMask;

	uMask = uCount;
	uMask <<= USEASM_OPFLAGS1_REPEAT_SHIFT;

	psInst->uFlags1 |= uMask;
}

#if 0
/*****************************************************************************
 FUNCTION	: GetRepeatMask

 PURPOSE	: Get an instructions repeat mask

 PARAMETERS	: psInst     - Instruction to query

 RETURNS	: Repeat mask for the instruction
*****************************************************************************/
static
IMG_UINT32 GetRepeatMask(PUSE_INST psInst)
{
	IMG_UINT32 uMask =  USE_UNDEF;

	uMask = psInst->uFlags1 & ~USEASM_OPFLAGS1_MASK_CLRMSK;
	uMask >>= USEASM_OPFLAGS1_MASK_SHIFT;

	return uMask;
}
#endif

/*****************************************************************************
 FUNCTION	: SetRepeatMask

 PURPOSE	: Set an instructions repeat mask

 PARAMETERS	: psInst     - Instruction
              uCount     - Repeat mask to set

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID SetRepeatMask(PUSE_INST psInst, IMG_UINT32 uMask)
{
	uMask <<= USEASM_OPFLAGS1_MASK_SHIFT;

	psInst->uFlags1 |= uMask;
}

/*****************************************************************************
 FUNCTION	: IsRepeating

 PURPOSE	: Test whether an instruction is repeating

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a repeating instruction
*****************************************************************************/
static
IMG_BOOL IsRepeating(PUSE_INST psInst)
{
	if (GetRepeatCount(psInst) > 1)
		return IMG_TRUE;
	else
		return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: IsMOEControl

 PURPOSE	: Test whether an instruction sets the MOE

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst can change the MOE
*****************************************************************************/
static
IMG_BOOL IsMOEControl(PUSE_INST psInst)
{
	switch(psInst->uOpcode)
	{
		case USEASM_OP_SMOA:
		case USEASM_OP_SMR:
		case USEASM_OP_SMLSI:
		case USEASM_OP_SMBO:
			return IMG_TRUE;
		default:
			return IMG_FALSE;
	}
}

/*****************************************************************************
 FUNCTION	: IsBranch

 PURPOSE	: Test whether an instruction is a branch

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a control flow instruction
*****************************************************************************/
static
IMG_BOOL IsBranch(PUSE_INST psInst)
{
	if (USEASM_OP_BA <= psInst->uOpcode &&
		psInst->uOpcode <= USEASM_OP_LAPC)
	{
		return IMG_TRUE;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: IsLabel

 PURPOSE	: Test whether an instruction is a label

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a label
*****************************************************************************/
static
IMG_BOOL IsLabel(PUSE_INST psInst)
{
	return (IMG_BOOL)(psInst->uOpcode == USEASM_OP_LABEL);
}

/*****************************************************************************
 FUNCTION	: IsBlockEnd

 PURPOSE	: Test whether an instruction ends a block

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a ends a block
*****************************************************************************/
static
IMG_BOOL IsBlockEnd(PUSE_INST psInst)
{
	if (psInst == NULL)
		return IMG_TRUE;
	return IsBranch(psInst);
}

/*****************************************************************************
 FUNCTION	: GetBranchLabel

 PURPOSE	: Get the target label of a relative branch instruction

 PARAMETERS	: psInst     - Instruction

 RETURNS	: The offset of the branch or  USE_UNDEF on failure.
*****************************************************************************/
static
IMG_UINT32 GetBranchLabel(PUSE_INST psInst)
{
	IMG_UINT32 uLabel =  USE_UNDEF;

	if (psInst->uOpcode != USEASM_OP_BR)
		return  USE_UNDEF;

	uLabel = psInst->asArg[0].uNumber;

	if (psInst->asArg[0].uType == USEASM_REGTYPE_LABEL_WITH_OFFSET)
		uLabel =  USE_UNDEF;

	return uLabel;
}

/*****************************************************************************
 FUNCTION	: IsTest

 PURPOSE	: Test whether an instruction is a test

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a test instruction
*****************************************************************************/
static
IMG_BOOL IsTest(PUSE_INST psInst)
{
	if (psInst->uFlags1 & USEASM_OPFLAGS1_TESTENABLE)
	{
		return IMG_TRUE;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: IsPack

 PURPOSE	: Test whether an instruction is a pack

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a pack instruction
*****************************************************************************/
static
IMG_BOOL IsPack(PUSE_INST psInst)
{
	if (USEASM_OP_PCKF16F32 <= psInst->uOpcode &&
		psInst->uOpcode <= USEASM_OP_UNPCKC10C10)
	{
		return IMG_TRUE;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: IsMultiPart

 PURPOSE	: Test whether instruction has a co-instruction

 PARAMETERS	: psInst     - Instruction

 RETURNS	: IMG_TRUE iff psInst has a co-instruction
*****************************************************************************/
static
IMG_BOOL IsMultiPart(PUSE_INST psInst)
{
	if (psInst == NULL)
		return IMG_FALSE;

	if (psInst->psNext != NULL)
	{
		const PUSE_INST psNext = psInst->psNext;
		if (psNext->uFlags2 & USEASM_OPFLAGS2_COISSUE)
		{
			return IMG_TRUE;
		}
	}

	switch(psInst->uOpcode)
	{
		case USEASM_OP_SOP2:
		case USEASM_OP_SOP3:
		{
			return IMG_TRUE;
		}
		default:
			break;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: GetWriteMask

 PURPOSE	: Get the write mask for an instruction

 PARAMETERS	: psInst     - Instruction

 RETURNS	: Write mask for the instruction
*****************************************************************************/
static
IMG_UINT32 GetWriteMask(PUSE_INST psInst)
{
	IMG_UINT32 uDestMask = USE_ALL_CHAN_MASK;

	if (IsPack(psInst) ||
		psInst->uOpcode == USEASM_OP_SOP2WM)
	{
		uDestMask = (psInst->asArg[0].uFlags & ~USEASM_ARGFLAGS_BYTEMSK_CLRMSK);
		uDestMask >>= USEASM_ARGFLAGS_BYTEMSK_SHIFT;
	}

	return uDestMask;
}

/*****************************************************************************
 FUNCTION	: IsFullWrite

 PURPOSE	: Test whether an instruction always writes to all channels

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE if psInst writes to all channels of the destination
              IMG_FALSE if psInst can do a partial write
*****************************************************************************/
static
IMG_BOOL IsFullWrite(PUSE_INST psInst)
{
	IMG_UINT32 uDestMask = 0;

	uDestMask = GetWriteMask(psInst);
	if (uDestMask != USE_ALL_CHAN_MASK)
	{
		return IMG_FALSE;
	}

	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: UsesSrc0

 PURPOSE	: Test whether an instruction uses src0

 PARAMETERS	: uOpcode    - Opcode of instruction to test

 RETURNS	: TRUE iff instruction is known to use src0
*****************************************************************************/
static
IMG_BOOL UsesSrc0(USEASM_OPCODE uOpcode)
{
	if (uOpcode == USEASM_OP_MOVC ||
		uOpcode == USEASM_OP_IMOV16 ||
		uOpcode == USEASM_OP_FADD ||
		uOpcode == USEASM_OP_FSUB ||
		uOpcode == USEASM_OP_FMUL ||
		uOpcode == USEASM_OP_FMAD ||
		uOpcode == USEASM_OP_FADM ||
		uOpcode == USEASM_OP_FMSA ||
		(uOpcode >= USEASM_OP_FDDP && uOpcode <= USEASM_OP_FDDPC4) ||
		uOpcode == USEASM_OP_EFO ||
		(uOpcode >= USEASM_OP_IMA8 && uOpcode <= USEASM_OP_IMA32) ||
		(uOpcode >= USEASM_OP_SMP1D && uOpcode <= USEASM_OP_SMP3DGRAD) ||
		(uOpcode >= USEASM_OP_SOP2 && uOpcode <= USEASM_OP_SOP3) ||
		(uOpcode >= USEASM_OP_LRP1 && uOpcode <= USEASM_OP_LRP2) ||
		uOpcode == USEASM_OP_FPMA ||
		uOpcode == USEASM_OP_FMINMAX ||
		uOpcode == USEASM_OP_FMAXMIN ||
		uOpcode == USEASM_OP_FSSQ ||
		uOpcode == USEASM_OP_IDIV)
	{
		return IMG_TRUE;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 NAME:    : InitSopSpec
 PURPOSE  : Initialise a sop specification

 INPUTS   : psSpec    - Specification to initialise

 RETURNS  : Nothing
*****************************************************************************/
IMG_INTERNAL
IMG_VOID InitSopSpec(PUSE_SOPSPEC psSpec)
{
	UseAsm_MemSet(psSpec, 0, sizeof(USE_SOPSPEC));

	psSpec->uOpcode = USE_OPCODE_UNDEF;

	psSpec->uWriteMask = 0;

	UseoptInitReg(&psSpec->sOut);
	UseoptInitReg(&psSpec->asSrc[0]);
	UseoptInitReg(&psSpec->asSrc[1]);
	UseoptInitReg(&psSpec->asSrc[2]);

	psSpec->uSrc1ColFactor = USEASM_INTSRCSEL_ZERO;
	psSpec->bOneMinusSrc1Col = IMG_FALSE;

	psSpec->uSrc1AlphaFactor = USEASM_INTSRCSEL_ZERO;
	psSpec->bOneMinusSrc1Alpha = IMG_FALSE;

	psSpec->uSrc2ColFactor = USEASM_INTSRCSEL_ZERO;
	psSpec->bOneMinusSrc2Col = IMG_FALSE;

	psSpec->uSrc2AlphaFactor = USEASM_INTSRCSEL_ZERO;
	psSpec->bOneMinusSrc2Alpha = IMG_FALSE;

	psSpec->uCop = USEASM_INTSRCSEL_ADD;
	psSpec->uAop = USEASM_INTSRCSEL_ADD;

	psSpec->bNegCop = IMG_FALSE;
	psSpec->bNegAop = IMG_FALSE;
}

/*****************************************************************************
 NAME:    : AllocSopSpec

 PURPOSE  : Allocate and initialise a sop specification

 INPUTS   : psState    - Opimiser state

 RETURNS  : An initialised sop specificiaotn
*****************************************************************************/
static
PUSE_SOPSPEC AllocSopSpec(PUSEOPT_STATE psState)
{
	PUSE_SOPSPEC psSpec;

	psSpec = UseoptAlloc(psState, sizeof(USE_SOPSPEC));
	InitSopSpec(psSpec);

	return psSpec;
}

/*****************************************************************************
 NAME:    : CompIntSrcSel

 PURPOSE  : Try to complement an INTSRCSEL selector

 INPUTS   : uSel      - Selector to complement
            bComp     - Complement value

 OUTPUTS  : puNewSel  - New selector

 RETURNS  : IMG_TRUE if selector could be changed
            IMG_FALSE if selector is not changable
*****************************************************************************/
static
IMG_BOOL CompIntSrcSel(USEASM_INTSRCSEL uSel,
					   IMG_BOOL bComp,
					   USEASM_INTSRCSEL *puNewSel)
{
	switch(uSel)
	{
		case USEASM_INTSRCSEL_ZERO:
		{
			if (bComp)
				(*puNewSel) = USEASM_INTSRCSEL_ONE;
			else
				(*puNewSel) = uSel;
			return IMG_TRUE;
		}
		case USEASM_INTSRCSEL_ONE:
		{
			if (bComp)
				(*puNewSel) = USEASM_INTSRCSEL_ZERO;
			else
				(*puNewSel) = uSel;
			return IMG_TRUE;
		}
		default:
			break;
	}
	return IMG_FALSE;
}

/*****************************************************************************
 Name:		MakeAlphaSel

 Purpose:	Use the alpha part of a sop selector

 Inputs:	uSel	- Useasm sop factor

 Returns:	Alpha part of the selector.
*****************************************************************************/
static
USEASM_INTSRCSEL MakeAlphaSel(USEASM_INTSRCSEL uSel)
{
	switch (uSel)
	{
		case USEASM_INTSRCSEL_SRC0:
			return USEASM_INTSRCSEL_SRC0ALPHA;
		case USEASM_INTSRCSEL_SRC1:
			return USEASM_INTSRCSEL_SRC1ALPHA;
		case USEASM_INTSRCSEL_SRC2:
			return USEASM_INTSRCSEL_SRC2ALPHA;
		default:
			return uSel;
	}
}

/*****************************************************************************
 NAME:    : ExtractSop2Spec

 PURPOSE  : Form a sop specification from a sop2 or sop2wm instruction

 INPUTS   : psInst    - Sop2 Instruction

 OUTPUTS  : psSpec	  - Sop specification
            ppsNext   - Instruction following the sop

 RETURNS  : IMG_TRUE if specification was formed
            IMG_FALSE if instruction is not recognised
*****************************************************************************/
static
IMG_BOOL ExtractSop2Spec(PUSE_INST psInst,
						 PUSE_SOPSPEC psSpec,
						 PUSE_INST *ppsNext)
{
	IMG_BOOL bFailed = IMG_FALSE;
	PUSE_INST psCoInst = NULL;
	USEASM_INTSRCSEL uTmpSel;
	USEASM_OPCODE uOpcode;
	IMG_UINT32 uIdx, uArg;
	PUSE_INST psNext = NULL;

	if (psInst->uOpcode == USEASM_OP_SOP2)
	{
		psCoInst = psInst->psNext;
		psNext = psCoInst->psNext;

		if (psCoInst == NULL ||
			(psCoInst->uOpcode != USEASM_OP_ASOP &&
			 psCoInst->uOpcode != USEASM_OP_ASOP2))
		{
			bFailed = IMG_TRUE;
			goto Exit;
		}
	}
	else if (psInst->uOpcode == USEASM_OP_SOP2WM)
	{
		psNext = psInst->psNext;
	}
	else
	{
		bFailed = IMG_TRUE;
		goto Exit;
	}

	InitSopSpec(psSpec);
	uOpcode = psInst->uOpcode;
	uArg = 0;

	/* Set the opcode */
	psSpec->uOpcode = uOpcode;

	/* Set the predicate */
	psSpec->uPred = GetPredicate(psInst);

	/* Set the write mask */
	psSpec->uWriteMask = GetWriteMask(psInst);

	/* Format control */
	if (psInst->uFlags2 & USEASM_OPFLAGS2_FORMATSELECT)
		psSpec->bFmtControl = IMG_TRUE;
	else
		psSpec->bFmtControl = IMG_FALSE;

	/* Set the destination */
	psSpec->sOut = psInst->asArg[uArg];
	uArg += 1;

	/* Set the sources */
	uIdx = 1;
	psSpec->asSrc[uIdx] = psInst->asArg[uArg];
	uArg +=1 ;
	psSpec->asSrc[uIdx + 1] = psInst->asArg[uArg];
	uArg += 1;

	/* Src colour modifier */
	if (uOpcode == USEASM_OP_SOP2)
	{
		if (psInst->asArg[uArg].uNumber == USEASM_INTSRCSEL_COMP)
			psSpec->bCompSrc1 = IMG_TRUE;
		else
			psSpec->bCompSrc1 = IMG_FALSE;

		uArg += 1;
	}

	/* Src1 colour factor */
	psSpec->uSrc1ColFactor = (USEASM_INTSRCSEL)(psInst->asArg[uArg].uNumber);
	if (psInst->asArg[uArg].uFlags & USEASM_ARGFLAGS_COMPLEMENT)
		psSpec->bOneMinusSrc1Col = IMG_TRUE;
	else
		psSpec->bOneMinusSrc1Col = IMG_FALSE;
	if (CompIntSrcSel(psSpec->uSrc1ColFactor,
					  psSpec->bOneMinusSrc1Col,
					  &uTmpSel))
	{
		psSpec->uSrc1ColFactor = uTmpSel;
		psSpec->bOneMinusSrc1Col = IMG_FALSE;
	}
	uArg += 1;

	/* Src2 colour factor */
	psSpec->uSrc2ColFactor = (USEASM_INTSRCSEL)(psInst->asArg[uArg].uNumber);
	if (psInst->asArg[uArg].uFlags & USEASM_ARGFLAGS_COMPLEMENT)
			psSpec->bOneMinusSrc2Col = IMG_TRUE;
	else
		psSpec->bOneMinusSrc2Col = IMG_FALSE;
	if (CompIntSrcSel(psSpec->uSrc2ColFactor,
					  psSpec->bOneMinusSrc2Col,
					  &uTmpSel))
	{
		psSpec->uSrc2ColFactor = uTmpSel;
		psSpec->bOneMinusSrc2Col = IMG_FALSE;
	}
	uArg += 1;

	/* Colour op */
	psSpec->uCop = (USEASM_INTSRCSEL)(psInst->asArg[uArg].uNumber);
	uArg += 1;

	/* Sop2wm */
	if (uOpcode == USEASM_OP_SOP2WM)
	{
		/* Set the alpha operation */
		psSpec->uAop = (USEASM_INTSRCSEL)(psInst->asArg[uArg].uNumber);
		uArg += 1;

		/* Get the alpha factors from the colour factors */
		psSpec->uSrc1AlphaFactor = MakeAlphaSel(psSpec->uSrc1ColFactor);
		psSpec->bOneMinusSrc1Alpha = psSpec->bOneMinusSrc1Col;

		psSpec->uSrc2AlphaFactor = MakeAlphaSel(psSpec->uSrc2ColFactor);
		psSpec->bOneMinusSrc2Alpha = psSpec->bOneMinusSrc2Col;

		/* Finished */
		bFailed = IMG_FALSE;
		goto Exit;
	}

	/* Sop2 */

	/* Negate colour op */
	if (psInst->asArg[uArg].uNumber == USEASM_INTSRCSEL_NEG)
		psSpec->bNegCop = IMG_TRUE;
	else
		psSpec->bNegCop = IMG_FALSE;
	uArg += 1;


	/* Co-Instruction */
	psCoInst = psInst->psNext;
	uArg = 0;

	/* Src1 alpha */
	if (psCoInst->asArg[uArg].uNumber == USEASM_INTSRCSEL_COMP)
		psSpec->bCompSrc1Alpha = IMG_TRUE;
	else
		psSpec->bCompSrc1Alpha = IMG_FALSE;
	uArg += 1;

	/* Src1 alpha factor */
	psSpec->uSrc1AlphaFactor = (USEASM_INTSRCSEL)(psCoInst->asArg[uArg].uNumber);
	if (psCoInst->asArg[uArg].uFlags & USEASM_ARGFLAGS_COMPLEMENT)
		psSpec->bOneMinusSrc1Alpha = IMG_TRUE;
	else
		psSpec->bOneMinusSrc1Alpha = IMG_FALSE;
	if (CompIntSrcSel(psSpec->uSrc1AlphaFactor,
					  psSpec->bOneMinusSrc1Alpha,
					  &uTmpSel))
	{
		psSpec->uSrc1AlphaFactor = uTmpSel;
		psSpec->bOneMinusSrc1Alpha = IMG_FALSE;
	}
	uArg += 1;

	/* Src2 alpha factor */
	psSpec->uSrc2AlphaFactor = (USEASM_INTSRCSEL)(psCoInst->asArg[uArg].uNumber);
	if (psCoInst->asArg[uArg].uFlags & USEASM_ARGFLAGS_COMPLEMENT)
		psSpec->bOneMinusSrc2Alpha = IMG_TRUE;
	else
		psSpec->bOneMinusSrc2Alpha = IMG_FALSE;
	if (CompIntSrcSel(psSpec->uSrc2AlphaFactor,
					  psSpec->bOneMinusSrc2Alpha,
					  &uTmpSel))
	{
		psSpec->uSrc2AlphaFactor = uTmpSel;
		psSpec->bOneMinusSrc2Alpha = IMG_FALSE;
	}
	uArg += 1;

	/* Alpha op */
	psSpec->uAop = (USEASM_INTSRCSEL)(psCoInst->asArg[uArg].uNumber);
	uArg += 1;

	/* Negate alpha op */
	if (psCoInst->asArg[uArg].uNumber == USEASM_INTSRCSEL_NEG)
		psSpec->bNegAop = IMG_TRUE;
	else
		psSpec->bNegAop = IMG_FALSE;
	uArg += 1;

  Exit:
    if (bFailed)
    {
        return IMG_FALSE;
    }

	if (ppsNext != NULL)
		(*ppsNext) = psNext;

    return IMG_TRUE;
}

/*****************************************************************************
 NAME:    : EncodeSop2Spec

 PURPOSE  : Form a sop2 or sop2wm instruction from a sop specification

 INPUTS   : psSpec	  - Sop specification

 OUTPUTS  : apsInst   - Two element array containing the sop instruction.
                        apsInst[0]: sop2 or sopwm
                        apsInst[1]: coinst or NULL

 NOTES    : apsInst is always overwritten

 RETURNS  : IMG_TRUE iff instruction was properly formed
*****************************************************************************/
static
IMG_BOOL EncodeSop2Spec(PUSE_SOPSPEC psSpec,
						PUSE_INST* apsInst)
{
	IMG_BOOL bFailed = IMG_FALSE;
	USEASM_OPCODE uOpcode;
	IMG_UINT32 uInstIdx;
	PUSE_INST psInst = NULL;
	IMG_BOOL bFormatControl;
	IMG_UINT32 uArg;
	IMG_UINT32 uIdx;

	uOpcode = psSpec->uOpcode;
	if (uOpcode != USEASM_OP_SOP2 &&
		uOpcode != USEASM_OP_SOP2WM)
	{
		bFailed = IMG_TRUE;
		goto Exit;
	}

	uInstIdx = 0;
	psInst = apsInst[uInstIdx];
	uArg = 0;

	/* Opcode */
	psInst->uOpcode = uOpcode;

	/* Format control */
	bFormatControl = psSpec->bFmtControl;
	if (psSpec->sOut.uFlags & USEASM_ARGFLAGS_FMTC10 ||
		psSpec->asSrc[1].uFlags & USEASM_ARGFLAGS_FMTC10 ||
		psSpec->asSrc[2].uFlags & USEASM_ARGFLAGS_FMTC10)
	{
		bFormatControl = IMG_TRUE;
	}

	if (bFormatControl)
		psInst->uFlags2 |= USEASM_OPFLAGS2_FORMATSELECT;

	/* Destination */
	psInst->asArg[uArg] = psSpec->sOut;

	/* Destination Mask */
	if (uOpcode == USEASM_OP_SOP2WM &&
		psSpec->uWriteMask != USE_ALL_CHAN_MASK)
	{
		IMG_UINT32 uMask;
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_BYTEMSK_PRESENT;
		uMask = psSpec->uWriteMask;
		uMask <<= USEASM_ARGFLAGS_BYTEMSK_SHIFT;
		uMask &= ~USEASM_ARGFLAGS_BYTEMSK_CLRMSK;
		psInst->asArg[uArg].uFlags |= uMask;
	}
	uArg += 1;

	/* Sources */
	uIdx = 1;
	psInst->asArg[uArg] = psSpec->asSrc[uIdx];
	uArg += 1;

	psInst->asArg[uArg] = psSpec->asSrc[uIdx + 1];
	uArg += 1;

	/* Src1 modifier */
	if (uOpcode == USEASM_OP_SOP2)
	{
		psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
		if (psSpec->bCompSrc1)
			psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_COMP;
		else
			psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NONE;
		uArg += 1;
	}

	/* Src1 colour factor */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uSrc1ColFactor;
	if (psSpec->bOneMinusSrc1Col)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	/* Src2 colour factor */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uSrc2ColFactor;
	if (psSpec->bOneMinusSrc2Col)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	/* Colour operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uCop;
	uArg += 1;

	/* Sopwm instruction ends with the alpha operation */
	if (uOpcode == USEASM_OP_SOP2WM)
	{
		/* Alpha operation */
		psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
		psInst->asArg[uArg].uNumber = psSpec->uAop;
		uArg += 1;

		apsInst[1] = NULL;
		goto Exit;
	}

	/*
	 * Keep going with sop2 instruction
	 */

	/* Negate colour operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	if (psSpec->bNegCop)
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NEG;
	else
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NONE;
	uArg += 1;

	/* Finished with sop2 instruction */
	apsInst[uInstIdx] = psInst;
	uInstIdx += 1;

	/* Form the co-instruction */
	psInst = apsInst[uInstIdx];
	uArg = 0;

	/* Opcode */
	psInst->uOpcode = USEASM_OP_ASOP2;

	/* Whether to negate src1alpha */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	if (psSpec->bCompSrc1Alpha)
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_COMP;
	else
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NONE;
	uArg += 1;

	/* Src1 alpha factor (ASel1) */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = MakeAlphaSel(psSpec->uSrc1AlphaFactor);
	if (psSpec->bOneMinusSrc1Alpha)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	/* Src2 alpha factor (ASel2) */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = MakeAlphaSel(psSpec->uSrc2AlphaFactor);
	if (psSpec->bOneMinusSrc2Alpha)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	/* Alpha operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uAop;
	uArg += 1;

	/* Negate the alpha operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	if (psSpec->bNegAop)
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NEG;
	else
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NONE;

	/* Finished with sop2 co-instruction */

	/* Co-issue the instructions */
	apsInst[0]->uFlags1 |= USEASM_OPFLAGS1_MAINISSUE;
	apsInst[0]->psNext = psInst;
	psInst->psPrev = apsInst[0];

  Exit:
	if (bFailed)
	{
		return IMG_FALSE;
	}

	return IMG_TRUE;
}

/*****************************************************************************
 NAME:    : EncodeSop3Spec

 PURPOSE  : Form a sop3  from a sop specification

 INPUTS   : psSpec	  - Sop specification

 OUTPUTS  : apsInst   - Two element array containing the sop instruction.
                        apsInst[0]: sop3 or sopwm
                        apsInst[1]: coinst

 NOTES    : apsInst is always overwritten

 RETURNS  : IMG_TRUE iff instruction was properly formed
*****************************************************************************/
static
IMG_BOOL EncodeSop3Spec(PUSE_SOPSPEC psSpec,
						PUSE_INST* apsInst)
{
	const IMG_UINT32 uValidFlags = USEASM_ARGFLAGS_FMTC10;
	IMG_BOOL bFailed = IMG_FALSE;
	USEASM_OPCODE uOpcode;
	IMG_UINT32 uInstIdx;
	PUSE_INST psInst = NULL;
	IMG_BOOL bFormatControl;
	IMG_UINT32 uArg;
	IMG_UINT32 uCtr;

	uOpcode = psSpec->uOpcode;
	if (uOpcode != USEASM_OP_SOP3)
	{
		bFailed = IMG_TRUE;
		goto Exit;
	}

	/*
	 * Main instruction
	 */

	uInstIdx = 0;
	psInst = apsInst[uInstIdx];
	uArg = 0;
	SetRepeatMask(psInst, 0x1);

	/* Opcode */
	psInst->uOpcode = uOpcode;

	/* Format control */
	bFormatControl = psSpec->bFmtControl;
	if (psSpec->sOut.uFlags & USEASM_ARGFLAGS_FMTC10 ||
		psSpec->asSrc[1].uFlags & USEASM_ARGFLAGS_FMTC10 ||
		psSpec->asSrc[2].uFlags & USEASM_ARGFLAGS_FMTC10)
	{
		bFormatControl = IMG_TRUE;
	}

	if (bFormatControl)
		psInst->uFlags2 |= USEASM_OPFLAGS2_FORMATSELECT;

	/* Destination */
	psInst->asArg[uArg] = psSpec->sOut;
	uArg += 1;

	/* Sources */
	for (uCtr = 0; uCtr < 3; uCtr ++)
	{
		psInst->asArg[uArg] = psSpec->asSrc[uCtr];
		psInst->asArg[uArg].uFlags &= uValidFlags;
		uArg += 1;
	}

	/* Src1 colour factor */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uSrc1ColFactor;
	if (psSpec->bOneMinusSrc1Col)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	/* Src2 colour factor */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uSrc2ColFactor;
	if (psSpec->bOneMinusSrc2Col)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	/* Colour operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uCop;
	uArg += 1;

	/* Negate colour operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	if (psSpec->bNegCop)
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NEG;
	else
		psInst->asArg[uArg].uNumber = USEASM_INTSRCSEL_NONE;
	uArg += 1;

	/* Finish the main instruction */
	apsInst[uInstIdx] = psInst;
	uInstIdx += 1;

	/*
	 * Co-Instruction
	 */

	/* Form the co-instruction */
	psInst = apsInst[uInstIdx];
	uArg = 0;

	/* Opcode */
	if (psSpec->bNegCop)
		psInst->uOpcode = USEASM_OP_ALRP;
	else if (psSpec->bNegAop)
		psInst->uOpcode = USEASM_OP_ARSOP;
	else
		psInst->uOpcode = USEASM_OP_ASOP;

	/* Src1 alpha factor (ASel1) */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = MakeAlphaSel(psSpec->uSrc1AlphaFactor);
	if (psSpec->bOneMinusSrc1Alpha)
	{
		psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
	}
	uArg += 1;

	if (psInst->uOpcode == USEASM_OP_ALRP)
	{
		/* Src2 alpha factor (ASel2) */
		psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
		psInst->asArg[uArg].uNumber = MakeAlphaSel(psSpec->uSrc2AlphaFactor);
		if (psSpec->bOneMinusSrc2Alpha)
		{
			psInst->asArg[uArg].uFlags |= USEASM_ARGFLAGS_COMPLEMENT;
		}
		uArg += 1;
	}

	/* Alpha operation */
	psInst->asArg[uArg].uType = USEASM_REGTYPE_INTSRCSEL;
	psInst->asArg[uArg].uNumber = psSpec->uAop;
	uArg += 1;

	/* Finished with co-instruction */
	apsInst[0]->uFlags1 |= USEASM_OPFLAGS1_MAINISSUE;
	apsInst[0]->psNext = psInst;
	psInst->psPrev = apsInst[0];

  Exit:
	if (bFailed)
	{
		return IMG_FALSE;
	}

	return IMG_TRUE;
}

/*****************************************************************************
 NAME:    : EncodeSopSpec

 PURPOSE  : Form a sop from a sop specification

 INPUTS   : psSpec	  - Sop specification

 OUTPUTS  : apsInst   - Two element array containing the sop instruction.
                        apsInst[0]: sop instruction
                        apsInst[1]: co-instruction or NULL

 NOTES    : apsInst is always overwritten

 RETURNS  : IMG_TRUE iff instruction was properly formed
*****************************************************************************/
IMG_INTERNAL
IMG_BOOL EncodeSopSpec(PUSE_SOPSPEC psSpec,
					   PUSE_INST* apsInst)
{
	if (psSpec == NULL)
	{
		return IMG_FALSE;
	}

	if (psSpec->uOpcode == USEASM_OP_SOP3)
		return EncodeSop3Spec(psSpec, apsInst);
	else
		return EncodeSop2Spec(psSpec, apsInst);
}

/*****************************************************************************
 FUNCTION	: IsMove

 PURPOSE	: Test whether an instruction is a move

 PARAMETERS	: psInst     - Instruction to test

 OUTPUT     : ppsDst     - Destination
              ppsSrc     - Source

 RETURNS	: IMG_TRUE iff psInst is a move instruction
*****************************************************************************/
static
IMG_BOOL IsMove(PUSE_INST psInst,
				PUSE_REGISTER *ppsDst,
				PUSE_REGISTER *ppsSrc)
{
	if (psInst == NULL)
		return IMG_FALSE;

	if (psInst->uOpcode == USEASM_OP_MOV ||
		psInst->uOpcode == USEASM_OP_FMOV)
	{
		if (ppsDst != NULL)
			(*ppsDst) = &psInst->asArg[0];

		if (ppsSrc != NULL)
			(*ppsSrc) = &psInst->asArg[1];

		return IMG_TRUE;
	}

	if (psInst->uOpcode == USEASM_OP_SOP2 ||
		(psInst->uOpcode == USEASM_OP_SOP2WM &&
		 GetWriteMask(psInst) == USE_ALL_CHAN_MASK))
	{
		PUSE_REGISTER psDst = NULL;
		PUSE_REGISTER psSrc = NULL;
		USE_SOPSPEC sSpec;
		IMG_BOOL bIsMove = IMG_FALSE;

		/* Got a possible sop move */
		if (!ExtractSop2Spec(psInst, &sSpec, NULL))
		{
			return IMG_FALSE;
		}

		/*
		  Test the operations
		*/
		if (!(sSpec.uCop == USEASM_INTSRCSEL_ADD &&
			  sSpec.uAop == USEASM_INTSRCSEL_ADD &&
			  !sSpec.bNegCop &&
			  !sSpec.bNegAop))
		{
			return IMG_FALSE;
		}

		/* Test for moves */
		psDst = &psInst->asArg[0];
		if (sSpec.uSrc1ColFactor == USEASM_INTSRCSEL_ONE &&
			sSpec.uSrc1AlphaFactor == USEASM_INTSRCSEL_ONE &&
			sSpec.uSrc2ColFactor == USEASM_INTSRCSEL_ZERO &&
			sSpec.uSrc2AlphaFactor == USEASM_INTSRCSEL_ZERO)
		{
			/* move from src1 */
			psSrc = &psInst->asArg[1];
			bIsMove = IMG_TRUE;
		}
		else if (sSpec.uSrc1ColFactor == USEASM_INTSRCSEL_ZERO &&
				 sSpec.uSrc1AlphaFactor == USEASM_INTSRCSEL_ZERO &&
				 sSpec.uSrc2ColFactor == USEASM_INTSRCSEL_ONE &&
				 sSpec.uSrc2AlphaFactor == USEASM_INTSRCSEL_ONE)
		{
			/* move from src2 */
			psSrc = &psInst->asArg[2];
			bIsMove = IMG_TRUE;
		}
		else
		{
			bIsMove = IMG_FALSE;
		}

		/* Test for modifiers in the source */
		if (bIsMove)
		{
			if ((psSrc->uFlags & USEASM_ARGFLAGS_NEGATE) ||
				(psSrc->uFlags & USEASM_ARGFLAGS_ABSOLUTE))
			{
					bIsMove = IMG_FALSE;
			}
		}
		/* Test for format changes */
		if (bIsMove)
		{
			const IMG_UINT32 uNumFmts = 4;
			const IMG_UINT32 auFmtFlag[4] =
			{
				USEASM_ARGFLAGS_FMTF32,
				USEASM_ARGFLAGS_FMTF16,
				USEASM_ARGFLAGS_FMTU8,
				USEASM_ARGFLAGS_FMTC10,
			};
			IMG_UINT32 uIdx = 0;
			for (uIdx = 0; uIdx < uNumFmts; ++uIdx)
			{
				const IMG_UINT32 uFlag = auFmtFlag[uIdx];

				if (((psDst->uFlags & uFlag) && (!(psSrc->uFlags & uFlag))) ||
					((!(psDst->uFlags & uFlag)) && (psSrc->uFlags & uFlag)))
				{
					bIsMove = IMG_FALSE;
					break;
				}
			}
		}

		/* Test for matching a move */
		if (bIsMove)
		{
			if (ppsDst != NULL)
				(*ppsDst) = &psInst->asArg[0];
			if (ppsSrc != NULL)
				(*ppsSrc) = psSrc;

			return IMG_TRUE;
		}
		else
		{
			return IMG_FALSE;
		}
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: CutList

 PURPOSE	: Cut a list into two

 PARAMETERS	: psCutPoint  - The point at which to cut the list

 RETURNS	: The head of the detached list (== psCutPoint)
*****************************************************************************/
static
PUSE_INST CutList(PUSE_INST psCutPoint)
{
	if (psCutPoint == NULL)
		return NULL;

	if (psCutPoint->psPrev != NULL)
	{
		psCutPoint->psPrev->psNext = NULL;
	}
	psCutPoint->psPrev = NULL;

	return psCutPoint;
}

/*****************************************************************************
 FUNCTION	: GetLastInst

 PURPOSE	: Get the last instruction in a list of instructions

 PARAMETERS	: psList    - List of instructions

 RETURNS	: Last instruction in the list
*****************************************************************************/
static
PUSE_INST GetLastInst(PUSE_INST psList)
{
	PUSE_INST psCurr;

	if (psList == NULL)
		return NULL;

	for (psCurr = psList; psCurr->psNext != NULL; psCurr = psCurr->psNext)
	{
		/* skip */
	}

	return psCurr;
}

/*****************************************************************************
 FUNCTION	: AppendList

 PURPOSE	: Append two lists

 PARAMETERS	: psState	- Optimiser state.
			  psFirst   - First list
              psSecond  - Second list

 RETURNS	: The new list
*****************************************************************************/
static
PUSE_INST AppendList(PUSEOPT_STATE psState,
					 PUSE_INST psFirst,
					 PUSE_INST psSecond)
{
	PUSE_INST psList = NULL;

	PVR_UNREFERENCED_PARAMETER(psState);

	if (psFirst == NULL)
	{
		return psSecond;
	}
	if (psSecond == NULL)
	{
		return psFirst;
	}

	psList = psFirst;
	psFirst = GetLastInst(psFirst);

	if (psFirst->psNext != NULL)
	{
		psFirst->psNext->psPrev = NULL;
	}
	psFirst->psNext = psSecond;

	if (psSecond->psPrev != NULL)
	{
		psSecond->psPrev->psNext = NULL;
	}
	psSecond->psPrev = psFirst;

	return psList;
}

/*****************************************************************************
 FUNCTION	: RemoveInst

 PURPOSE	: Remove an instruction from its code block

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Code block
              psInst     - Instruction to remove

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID RemoveInst(PUSEOPT_STATE psState,
					PUSE_BLOCK psBlock,
					PUSE_INST psInst)
{
	PVR_UNREFERENCED_PARAMETER(psState);

	ENSURE(psState, psBlock != NULL);

	if (psInst == NULL)
		return;

	if (psBlock->psFirst == psInst)
	{
		psBlock->psFirst = psInst->psNext;
	}

	if (psBlock->psLast == psInst)
	{
		psBlock->psLast = psInst->psPrev;
	}

	if (psInst->psPrev != NULL)
	{
		psInst->psPrev->psNext = psInst->psNext;
	}
	if (psInst->psNext != NULL)
	{
		psInst->psNext->psPrev = psInst->psPrev;
	}

	psInst->psPrev = NULL;
	psInst->psNext = NULL;
}

/*****************************************************************************
 FUNCTION	: GetNextInst

 PURPOSE	: Get the next full instruction, skipping co-instructions

 PARAMETERS	: psInst     - Instruction

 RETURNS	: Instruction following the instruction
*****************************************************************************/
static
PUSE_INST GetNextInst(PUSE_INST psInst)
{
	PUSE_INST psNext;

	if (psInst == NULL)
		return NULL;

	psNext = psInst->psNext;
	if (IsMultiPart(psInst))
	{
		psNext = psNext->psNext;
	}

	return psNext;
}

/*****************************************************************************
 FUNCTION	: GetInstStart

 PURPOSE	: Get the start of the instruction, taking account of co-instructions

 PARAMETERS	: psInst     - Instruction

 RETURNS	: Instruction following the instruction
*****************************************************************************/
static
PUSE_INST GetInstStart(PUSE_INST psInst)
{
	PUSE_INST psPrev;

	if (psInst == NULL)
		return NULL;

	psPrev = psInst->psPrev;

	if (IsMultiPart(psPrev))
	{
		return psPrev;
	}

	return psInst;
}

/*****************************************************************************
 FUNCTION	: DeleteInst

 PURPOSE	: Delete a instruction, and any co-instruction, from a code block

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Code block
              psInst     - Instruction to remove

 RETURNS	: Instruction following the deleted instruction

 NOTES      : Frees the instructions memory
*****************************************************************************/
static
PUSE_INST DeleteInst(PUSEOPT_STATE psState,
					 PUSE_BLOCK psBlock,
					 PUSE_INST psInst)
{
	PUSE_INST psNext = psInst->psNext;
	PUSE_INST psRet = psNext;
	IMG_BOOL bIsMultiPart = IMG_FALSE;

	if (psInst == NULL)
		return NULL;
	bIsMultiPart = IsMultiPart(psInst);

	RemoveInst(psState, psBlock, psInst);
	UseoptFree(psState, psInst);

	if (bIsMultiPart)
	{
		psRet = psNext->psNext;

		RemoveInst(psState, psBlock, psNext);
		UseoptFree(psState, psNext);
	}

	return psRet;
}

/*****************************************************************************
 FUNCTION	: InsertBefore

 PURPOSE	: Insert an instruction before an instruction

 PARAMETERS	: psState	- Optimiser state.
			  psInst    - Instruction to insert
              psPoint   - Instruction to insert before

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID InsertBefore(PUSEOPT_STATE psState,
					  PUSE_BLOCK psBlock,
					  PUSE_INST psInst,
					  PUSE_INST psPoint)
{
	PVR_UNREFERENCED_PARAMETER(psState);

	if (psInst == NULL)
	{
		return;
	}

	if (psBlock->psFirst == NULL)
	{
		psBlock->psFirst = psInst;
		psBlock->psLast = psInst;

		return;
	}

	if (psPoint != NULL)
	{
		psInst->psPrev = psPoint->psPrev;
		if (psInst->psPrev != NULL)
			psInst->psPrev->psNext = psInst;

		psInst->psNext = psPoint;
		psPoint->psPrev = psInst;

		if (psBlock->psFirst == psPoint)
		{
			psBlock->psFirst = psInst;
		}

	}
	else
	{
		psPoint = psBlock->psLast;

		psInst->psNext = NULL;
		psPoint->psNext = psInst;
		psInst->psPrev = psPoint;

		psBlock->psLast = psInst;
	}
}

/*****************************************************************************
 FUNCTION	: EmitMove

 PURPOSE	: Emit a move instruction

 PARAMETERS	: psState	- Optimiser state.
			  psDst     - Destination register
              psSrc     - Source register
              uFlags    - Flags to set

 RETURNS	: The new instruction
*****************************************************************************/
static
PUSE_INST EmitMove(PUSEOPT_STATE psState,
				   PUSE_REGISTER psDst,
				   PUSE_REGISTER psSrc,
				   IMG_UINT32 uFlags)
{
	const IMG_UINT32 uValidDstFlags = 0;
	const IMG_UINT32 uValidSrcFlags = 0;
	const IMG_UINT32 uValidInstFlags = ((1 << USE_SKIPINV) | (1 << USE_NOSCHED));
	PUSE_INST psInst = NULL;

	psInst = (PUSE_INST)UseoptAlloc(psState, sizeof(USE_INST));
	if (psInst == NULL)
	{
		return NULL;
	}
	UseAsmInitInst(psInst);

	psInst->uOpcode = USEASM_OP_MOV;

	UseoptInitReg(&(psInst->asArg[0]));
	psInst->asArg[0].uType = psDst->uType;
	psInst->asArg[0].uNumber = psDst->uNumber;
	psInst->asArg[0].uFlags &= uValidDstFlags;

	UseoptInitReg(&(psInst->asArg[1]));
	psInst->asArg[1].uType = psSrc->uType;
	psInst->asArg[1].uNumber = psSrc->uNumber;
	psInst->asArg[1].uFlags &= uValidSrcFlags;

	uFlags &= uValidInstFlags;
	SetInstFlags(psInst, uFlags, IMG_TRUE);
	SetRepeatCount(psInst, 1);

	return psInst;
}

/*****************************************************************************
 FUNCTION	: CanUseSrc

 PURPOSE	: Check if an instruction can accept a register type in an argument.

 PARAMETERS	: psState	- Compiler state.
			  psInst	- Instruction to check.
			  uArg		- The argument to check.
			  uType		- The register type.

 RETURNS	: TRUE iff instruction is known to support the register in the
              source operand position
*****************************************************************************/
static
IMG_BOOL CanUseSrc(PUSEOPT_STATE psState,
				   PUSE_INST psInst,
				   IMG_UINT32 uArg,
				   USEASM_REGTYPE uType,
				   IMG_UINT32 uIndex)
{
	IMG_BOOL bSrc0 = IMG_FALSE;
	USEASM_OPCODE uOpcode;

	if (psInst == NULL)
	{
		return IMG_FALSE;
	}
	uOpcode = psInst->uOpcode;

	/* An index register can never be a source */
	if (uType == USEASM_REGTYPE_INDEX)
	{
		return IMG_FALSE;
	}
	if (uOpcode == USEASM_OP_EFO)
	{
		IMG_BOOL	bValidInternalBank;

		bValidInternalBank = IMG_FALSE;
		if (uType == USEASM_REGTYPE_FPINTERNAL)
		{
			bValidInternalBank = IMG_TRUE;
		}
		if (uType != USEASM_REGTYPE_TEMP &&
			uType != USEASM_REGTYPE_OUTPUT &&
			uType != USEASM_REGTYPE_PRIMATTR &&
			uType != USEASM_REGTYPE_SECATTR &&
			!bValidInternalBank)
		{
			return IMG_FALSE;
		}
		if (uIndex != USEREG_INDEX_NONE)
		{
			return IMG_FALSE;
		}
	}
	if (uOpcode == USEASM_OP_ELDD || uOpcode == USEASM_OP_ELDQ)
	{
		if (uArg == 1)
		{
			if (uType == USEASM_REGTYPE_TEMP ||
				uType == USEASM_REGTYPE_PRIMATTR)
			{
				return IMG_TRUE;
			}
			return IMG_FALSE;
		}
		else
		{
			if (uType == USEASM_REGTYPE_TEMP ||
				uType == USEASM_REGTYPE_PRIMATTR ||
				uType == USEASM_REGTYPE_OUTPUT ||
				uType == USEASM_REGTYPE_SECATTR)
			{
				return IMG_TRUE;
			}
			return IMG_FALSE;
		}
	}
	if (uOpcode == USEASM_OP_FDSX || uOpcode == USEASM_OP_FDSY)
	{
		if (uType != USEASM_REGTYPE_TEMP &&
			uType != USEASM_REGTYPE_PRIMATTR)
		{
			return IMG_FALSE;
		}
	}
	if (uArg == 1 &&
		(uOpcode == USEASM_OP_FPADD8 || uOpcode == USEASM_OP_FPSUB8))
	{
		if (uType != USEASM_REGTYPE_TEMP &&
			uType != USEASM_REGTYPE_OUTPUT &&
			uType != USEASM_REGTYPE_PRIMATTR &&
			uType != USEASM_REGTYPE_SECATTR &&
			uType != USEASM_REGTYPE_FPINTERNAL)
		{
			return IMG_FALSE;
		}
		if (uIndex != USEREG_INDEX_NONE)
		{
			return IMG_FALSE;
		}
	}
	if ((uArg == 0 || uArg == 1) &&
		(uOpcode == USEASM_OP_SOP2 || uOpcode == USEASM_OP_SOP2WM))
	{
		if (uType != USEASM_REGTYPE_TEMP &&
			uType != USEASM_REGTYPE_OUTPUT &&
			uType != USEASM_REGTYPE_PRIMATTR &&
			uType != USEASM_REGTYPE_SECATTR &&
			uType != USEASM_REGTYPE_FPINTERNAL)
		{
			return IMG_FALSE;
		}
		if (uIndex != USEREG_INDEX_NONE)
		{
			return IMG_FALSE;
		}

		return IMG_TRUE;
	}

	if (uArg != 0)
	{
		return IMG_TRUE;
	}

	/* Test for use of extended banks in src0 */
	if (uType == USEASM_REGTYPE_TEMP ||
		uType == USEASM_REGTYPE_PRIMATTR)
	{
		return IMG_TRUE;
	}

	/*
		TEST instructions always use SRC1 and SRC2 regardless of the opcode.
	*/
	if (IsTest(psInst))
	{
		return IMG_TRUE;
	}

	bSrc0 = UsesSrc0(uOpcode);
	/*
		On SGX545 this instruction will be expanded into a minmax instruction.
	*/
	if (IsFClampInstruction(UseAsmGetCoreDesc(psState->psTarget)) &&
		(uOpcode == USEASM_OP_FMINMAX || uOpcode == USEASM_OP_FMAXMIN))
	{
		bSrc0 = IMG_TRUE;
	}
	/*
		Sources 1 and 2 support any register bank.
	*/
	if (!bSrc0)
	{
		return IMG_TRUE;
	}
	/*
		No indexed registers in source 0.
	*/
	if (uIndex != USEREG_INDEX_NONE)
	{
		return IMG_FALSE;
	}
	/*
		Temporary registers or primary attributes are valid in source 0.
	*/
	if (uType == USEASM_REGTYPE_TEMP || uType == USEASM_REGTYPE_PRIMATTR)
	{
		return IMG_TRUE;
	}
	/*
		Internal registers are valid in source 0.
	*/
	if (uType == USEASM_REGTYPE_FPINTERNAL)
	{
		return IMG_TRUE;
	}
	/*
		Otherwise invalid.
	*/
	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: FindRegRepl

 PURPOSE	: Get the replacment for a register

 PARAMETERS	: psState    - Optimiser state
              psReg      - Register to replace

 OUTPUT     : psRepl     - Replacment

 RETURNS	: IMG_TRUE iff a replacment was found
*****************************************************************************/
static
IMG_BOOL FindRegRepl(PUSEOPT_STATE psState,
					 PUSE_REGISTER psReg,
					 PUSE_REGISTER *ppsRepl)
{
	IMG_UINT32 uNode;
	PUSE_REGISTER psRepl = NULL;
	IMG_BOOL bFound = IMG_FALSE;
	IMG_BOOL bDone = IMG_FALSE;

	while (!bDone)
	{
		PUSE_REGISTER psTmp = NULL;

		if (!IsNode(psState, psReg))
		{
			/* Done */
			break;
		}
		uNode = RegToNode(psState, psReg);
		psTmp = &(psState->asNodeRepl[uNode].sReg);
		if (!(psTmp->uType < USEASM_REGTYPE_MAXIMUM))
		{
			/* Done */
			break;
		}

		psRepl = psTmp;
		bFound = IMG_TRUE;
		psReg = psRepl;
	}

	if (ppsRepl != NULL)
		(*ppsRepl) = psRepl;

	return bFound;
}

/*****************************************************************************
 FUNCTION	: FirstSrcIdx

 PURPOSE	: Get the index of the first source operand

 PARAMETERS	: psInst     - Instruction

 RETURNS	: Index of first source operand
*****************************************************************************/
static
IMG_UINT32 FirstSrcIdx(PUSE_INST psInst)
{
    IMG_UINT32 uIdx = 1;

    if (IsTest(psInst))
        uIdx = 2;

    return uIdx;
}

/*****************************************************************************
 FUNCTION	: SubstInstSrc

 PURPOSE	: Apply Substitutions for source operands

 PARAMETERS	: psState    - Optimiser state
              psInst     - Instruction to substitute into

 NOTES      : May introduce new instructions before psInst

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL SubstInstSrc(PUSEOPT_STATE psState,
                      PUSE_BLOCK psBlock,
					  PUSE_INST psInst)
{
	IMG_UINT32 uCtr, uNext;
	IMG_UINT32 uArgCount;

	if (psInst == NULL)
		return IMG_FALSE;

	uArgCount = UseGetOpcodeArgumentCount(psInst->uOpcode);
	uNext = uArgCount;

	for (uCtr = FirstSrcIdx(psInst);  // Ignore destination
		 uCtr < uArgCount;
		 uCtr = uNext)
	{
		PUSE_REGISTER psReg = &(psInst->asArg[uCtr]);
		PUSE_REGISTER psRepl = NULL;
		IMG_UINT32 uNode =  USE_UNDEF;

		uNext = uCtr + 1;

		/* Test for end of arguments */
		if (!(psReg->uType < USEASM_REGTYPE_MAXIMUM))
		{
			break;
		}
		/* Test for unknown register */
		if (!IsNode(psState, psReg))
		{
			continue;
		}

		/* Get the replacement, if any */
		uNode = RegToNode(psState, psReg);
		if (!FindRegRepl(psState, psReg, &psRepl))
		{
			continue;
		}
		/* Test for valid replacement */
		if (psRepl == NULL)
		{
			continue;
		}
		/* Try to replace register */
		if (CanUseSrc(psState, psInst, uCtr - 1,
					  psRepl->uType, psRepl->uIndex))
		{
			psReg->uType = psRepl->uType;
			psReg->uNumber = psRepl->uNumber;
			psReg->uIndex = psRepl->uIndex;
		}
		else
		{
			PUSE_INST psTmp = NULL;
			IMG_UINT32 uInstFlags = 0;

			/* Need to restore the move to the register */
			uInstFlags = psState->asNodeRepl[uNode].uInstFlags;

			psTmp = EmitMove(psState, psReg, psRepl, uInstFlags);
			if (psTmp == NULL)
			{
				/* Failed */
				return IMG_FALSE;
			}
            InsertBefore(psState, psBlock, psTmp, psInst);
			/* Remove the register from the list of replacements */
			psState->asNodeRepl[uNode].sReg.uType = USE_REGTYPE_UNDEF;
		}
	}

	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: SetRegRepl

 PURPOSE	: Set the replacement for a register

 PARAMETERS	: psState    - Optimiser state
              psReg      - Register
              psRepl     - Its replacment

 RETURNS	: IMG_TRUE if psRepl could be set as a replacement for psReg
*****************************************************************************/
static
IMG_BOOL SetRegRepl(PUSEOPT_STATE psState,
					PUSE_REGISTER psReg,
					PUSE_REGISTER psRepl)
{
	IMG_UINT32 uNode;

	if (!IsNode(psState, psReg))
	{
		return IMG_FALSE;
	}
	uNode = RegToNode(psState, psReg);
	psState->asNodeRepl[uNode].sReg = *psRepl;

	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: UpdateNodeRepl

 PURPOSE	: Update the node replacements

 PARAMETERS	: psState    - Optimiser state
              psInst     - Instruction

 RETURNS	: IMG_TRUE iff instructions were added
*****************************************************************************/
static
IMG_BOOL UpdateNodeRepl(PUSEOPT_STATE psState,
						PUSE_BLOCK psBlock,
						PUSE_INST psInst)
{
	IMG_UINT32 uNode =  USE_UNDEF;
	IMG_UINT32 uCtr;
	PUSE_REGISTER apsDst[2] = {NULL, NULL};

	if (psInst == NULL)
		return IMG_TRUE;

	apsDst[0] = &(psInst->asArg[0]);
	if (IsTest(psInst))
		apsDst[1] = &(psInst->asArg[1]);

	/* Test whether destination is replacable */
	for (uCtr = 0; uCtr < 2; uCtr +=1)
	{
		if (!IsNode(psState, apsDst[uCtr]))
		{
			continue;
		}

		uNode = RegToNode(psState, apsDst[uCtr]);

		/* Test for conditional write to the register */
		if (psState->asNodeRepl[uNode].sReg.uType < USEASM_REGTYPE_MAXIMUM &&
			GetPredicate(psInst) != USEASM_PRED_NONE)
		{
			PUSE_INST psMove = NULL;
			IMG_UINT32 uInstFlags = 0;
			PUSE_REGISTER psRepl = NULL;

			/* Need to restore the move before the predicated write */
			psRepl = &psState->asNodeRepl[uNode].sReg;
			uInstFlags = psState->asNodeRepl[uNode].uInstFlags;

			psMove = EmitMove(psState, apsDst[uCtr], psRepl, uInstFlags);
			InsertBefore(psState, psBlock, psMove, psInst);
		}
		/* Clobber existing record */
		psState->asNodeRepl[uNode].sReg.uType = USE_REGTYPE_UNDEF;
	}
	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: GetNodeRepl

 PURPOSE	: Get a replacement for a node

 PARAMETERS	: psState    - Optimiser state
              psInst     - Instruction

 RETURNS	: IMG_TRUE iff instruction was removed
*****************************************************************************/
static
IMG_BOOL GetNodeRepl(PUSEOPT_STATE psState,
					 PUSE_INST psInst)
{
	IMG_UINT32 uNode;
	PUSE_REGISTER psDst;
	PUSE_REGISTER psRepl;

	if (psInst == NULL)
		return IMG_FALSE;
	psDst = &(psInst->asArg[0]);

	/* Test whether destination is replacable */
	if (!IsNode(psState, psDst))
		return IMG_FALSE;
	uNode = RegToNode(psState, psDst);

	/* Try to get a replacement for the destination */
	if (IsMove(psInst, NULL, &psRepl) &&
		GetPredicate(psInst) == USEASM_PRED_NONE &&
		(UseoptGetBit(psState->auFlags, OPTFLAG_IGNORE_OUTREG) ||
		 !IsOutput(psState, psDst)))
	{
		/* Replace the node */
		IMG_UINT32 uInstFlags = 0;
		psState->asNodeRepl[uNode].sReg = *psRepl;

		uInstFlags = GetInstFlags(psInst);
		psState->asNodeRepl[uNode].uInstFlags = uInstFlags;

		return IMG_TRUE;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: ExtractNodeRepl

 PURPOSE	: Extract replacements for a node from an instruction

 PARAMETERS	: psState    - Optimiser state
              psInst     - Instruction to use
              bRemove    - Whether to remove the instruction on success

 OUTPUT     : pbChanged  - Whether the instruction was removed

 RETURNS	: The new instruction
*****************************************************************************/
static
PUSE_INST ExtractNodeRepl(PUSEOPT_STATE psState,
						  PUSE_BLOCK psBlock,
						  PUSE_INST psInst,
						  IMG_BOOL bRemove,
						  IMG_PBOOL pbChanged)
{
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_INST psRet = psInst;
	PUSE_REGISTER psDst, psSrc;

	if (IsMove(psInst, &psDst, &psSrc))
	{
		if (IsNode(psState, psDst))
		{
			/* Replace destination with source where possible */
			PUSE_INST psNext = GetNextInst(psInst);

			if (GetNodeRepl(psState, psInst))
			{
				if (bRemove)
				{
					/* Remove instruction */
					psNext = DeleteInst(psState, psBlock, psInst);
					psRet = psNext;
					bChanged = IMG_TRUE;
				}
			}
		}
	}

	if (pbChanged)
		(*pbChanged) = bChanged;
	return psRet;
}

/*****************************************************************************
 FUNCTION	: MunchMovs

 PURPOSE	: Eliminate a sequence of mov instructions, updating
              register replacement records, optionally finding a register.

 PARAMETERS	: psState     - Optimiser state
              psBlock     - Code block
              psInst      - First instruction in list
              psTrackReg  - Register to track

 OUTPUT     : psRepl      - Replacement for merge register
              pbProgEnd   - Removed the program end
              ppsNextInst - Instruction after the removed sequence

 RETURNS	: IMG_TRUE iff any change was made.

 NOTES      : Instructions with the program-end flag set will be removed
              iff pbProgEnd != NULL
*****************************************************************************/
static
IMG_BOOL MunchMovs(PUSEOPT_STATE psState,
				   PUSE_BLOCK psBlock,
				   PUSE_INST psInst,
				   PUSE_REGISTER psTrackReg,
				   PUSE_REGISTER psRepl,
				   IMG_PBOOL pbProgEnd,
				   PUSE_INST *ppsNextInst)
{
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_INST psCurr = NULL;
	PUSE_INST psNext = NULL;
	PUSE_INST psNextInst = NULL;
	IMG_BOOL bProgEnd = IMG_FALSE;

	/*
	  [ inst dst, ... ]
	  mov .., ..
	  mov r, dst
	  mov .., ..

	  -->
	  [ inst r, ... ]
	  mov .., ..
	  mov r, dst
	  mov .., ..

	 */

	psNextInst = NULL;
	bChanged = IMG_FALSE;

	if (psInst == NULL)
	{
		goto Exit;
	}

	psNextInst = GetNextInst(psInst);
	if (!IsMove(psInst, NULL, NULL))
	{
		goto Exit;
	}

	if (psRepl == NULL)
		psTrackReg = NULL;

	psNext = NULL;
	for (psCurr = psInst;
		 psCurr != NULL;
		 psCurr = psNext)
	{
		IMG_BOOL bTmpProgEnd = IMG_FALSE;
		IMG_BOOL bTrackSrc = IMG_FALSE;
		PUSE_REGISTER psDst, psSrc;
		psNext = GetNextInst(psCurr);

		if (!IsMove(psCurr, NULL, NULL))
		{
			/* Done */
			psNextInst = psCurr;
			break;
        }

        /* Apply source replacements */
		if (!SubstInstSrc(psState, psBlock, psCurr))
		{
			psNextInst = psCurr;
			goto Exit;
		}

		/* Test for register being clobbered */
		UpdateNodeRepl(psState, psBlock, psCurr);

		/* Don't remove program end instruction */
		if (IsProgEnd(psCurr))
		{
			/* Test whether the program end can be propagated up */
			if (pbProgEnd != NULL)
			{
				bTmpProgEnd = IMG_TRUE;
			}
			else
			{
				psNextInst = psCurr;
				break;
			}
		}

		/* Get the destination and updated source operands */
		IsMove(psCurr, &psDst, &psSrc);

		/* Check for null move (mov x, x) */
		if (IsNode(psState, psDst) &&
			EqualRegs(IMG_TRUE, psDst, psSrc))
		{
			/* Remove the null move */
			psNext = DeleteInst(psState, psBlock, psCurr);

			bChanged = IMG_TRUE;
			psNextInst = psNext;
			continue;
		}

        /* Test for instructions which are not handled */
		if (GetPredicate(psCurr) != USEASM_PRED_NONE ||
			GetRepeatCount(psCurr) > 1)
		{
			psNextInst = GetNextInst(psCurr);
			break;
        }

		/* Keep track of changes to register of interest */
		bTrackSrc = IMG_FALSE;
		if (psTrackReg != NULL)
		{
			if (EqualRegs(IMG_TRUE, psTrackReg, psSrc))
			{
				/* Found a match for the merge register */
				bTrackSrc = IMG_TRUE;
			}
		}

		/* Extract sources from the move and remove it */
		{
			USE_REGISTER sTmpReg;
			PUSE_INST psTmp = NULL;
			IMG_BOOL bMovRemoved = IMG_FALSE;
			IMG_BOOL bRemove = IMG_TRUE;

			if (bTrackSrc)
				sTmpReg = (*psDst);
			else
				UseAsm_MemSet(&sTmpReg, 0, sizeof(sTmpReg));

			psTmp = ExtractNodeRepl(psState, psBlock, psCurr,
									bRemove, &bMovRemoved);
			if (bMovRemoved)
			{
				bChanged = IMG_TRUE;
				psNext = psTmp;
				bProgEnd = bTmpProgEnd;

				if (bTrackSrc)
				{
					(*psRepl) = sTmpReg;
				}
			}
			else
			{
				/* Stop tracking registers */
				psTrackReg = NULL;
			}
		}
		psNextInst = psNext;
	}
	/* Push the program end bit forward */
	if (bProgEnd && psNextInst != NULL)
	{
		SetInstFlags(psNextInst, (1 << USE_PROGEND), IMG_TRUE);
		bProgEnd = IMG_FALSE;
	}

  Exit:
	if (pbProgEnd != NULL)
		(*pbProgEnd) = bProgEnd;
	if (ppsNextInst != NULL)
		(*ppsNextInst) = psNextInst;

	return bChanged;
}

/*****************************************************************************
 FUNCTION	: SetRegValues

 PURPOSE	: Generate instructions to set the values of registers that have been
              folded into node substitution.

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Code block
              psPoint    - Insertion point
              bProgEnd   - Generate for program end

 RETURNS	: Nothing
*****************************************************************************/
static
IMG_VOID SetRegValues(PUSEOPT_STATE psState,
					  PUSE_BLOCK psBlock,
					  PUSE_INST psPoint,
					  IMG_BOOL bProgEnd)
{
	IMG_UINT32 uNode;

	for (uNode = 0; uNode < psState->uNumNodes; uNode ++)
	{
		USE_REGISTER sReg;
		PUSE_REGISTER psRepl;
		PUSE_INST psMove = NULL;
		IMG_UINT32 uInstFlags = 0;

		if (!(psState->asNodeRepl[uNode].sReg.uType < USEASM_REGTYPE_MAXIMUM))
		{
			/* No value set for this node */
			continue;
		}

		if (bProgEnd && !(UseoptGetBit(psState->auOutputNode, uNode)))
		{
			/* Only care about the outputs at the program end */
			continue;
		}

		/* Emit the move to the node from its replacement */
		UseoptInitReg(&sReg);
		NodeToReg(psState, uNode, &sReg.uType, &sReg.uNumber);

		psRepl = &(psState->asNodeRepl[uNode].sReg);
		uInstFlags = psState->asNodeRepl[uNode].uInstFlags;

		psMove = EmitMove(psState, &sReg, psRepl, uInstFlags);
		InsertBefore(psState, psBlock, psMove, psPoint);

		/* Clobber the replacement value */
		psState->asNodeRepl[uNode].sReg.uType = USE_REGTYPE_UNDEF;
	}
}

/*****************************************************************************
 FUNCTION	: Sop2AddMult

 PURPOSE	: Reduce a sop2 add-multiply

 PARAMETERS	: psState    - Optimiser state
              psInst     - Sop2/Sop2wm instruction
              psNext     - Next instruction
              psSpec     - Specification of the instruction

 OUTPUT     : ppsNewInst - New instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL Sop2AddMult(PUSEOPT_STATE psState,
					 PUSE_BLOCK psBlock,
					 PUSE_INST psInst,
					 PUSE_INST psNext,
					 PUSE_SOPSPEC psAddSpec,
					 PUSE_INST *ppsNewInst)
{
	const IMG_UINT32 uNumSpecs = 2;
	IMG_BOOL bChanged = IMG_FALSE;
	IMG_BOOL bMatch = IMG_FALSE;
	IMG_UINT32 uFacIdx =  USE_UNDEF;
	PUSE_SOPSPEC psAddSop = NULL;
	PUSE_SOPSPEC psMultSop = NULL;
	PUSE_SOPSPEC psNewSop = NULL;
	PUSE_SOPSPEC psMultSpec = NULL;
	PUSE_SOPSPEC psSpec = NULL;
	PUSE_INST psNext1 = NULL;
	PUSE_SOPSPEC apsSpec[2] = {NULL, NULL};

	/*
	 * Combine integer mult and add (from libcombiner).
	 *
	 * add:  sop2 z, x, y, one, one, +, one, one, +
	 *       (z = [x + y])
	 * mult: sop2 d, a, z, src2, zero, +, src2, zero, +
     *       [d = (a * z)]
	 * -->
	 * mad:  sop3 d, a, x, y, src0, src0, +, src0, +
	 *       ( d = [(a * x) + (a * y)])
	 */

	bMatch = IMG_TRUE;

	/* Test the first sop for being an add */
    psSpec = psAddSpec;
    ENSURE(psState,
		   psSpec->uOpcode == USEASM_OP_SOP2 ||
		   psSpec->uOpcode == USEASM_OP_SOP2WM);

	/* Get the next sop */
	if (bMatch && psNext == NULL)
	{
		bMatch = IMG_FALSE;
	}

	apsSpec[0] = AllocSopSpec(psState);
	if (bMatch && !ExtractSop2Spec(psNext, apsSpec[0], &psNext1))
	{
		bMatch = IMG_FALSE;
	}
	psMultSpec = apsSpec[0];

	/* Test the first sop for addition */
	if (bMatch &&
		psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uCop == USEASM_INTSRCSEL_ADD &&
		psSpec->uAop == USEASM_INTSRCSEL_ADD &&
		(!psSpec->bNegCop) &&
		(!psSpec->bNegAop))
	{
		psAddSop = psSpec;
	}
	else
	{
		bMatch = IMG_FALSE;
	}

	/* Test the second sop for multiplication */
	psSpec = psMultSpec;
	if (bMatch &&
		psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_SRC2 &&
		psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ZERO &&
		psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_SRC2ALPHA &&
		psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ZERO &&
		psSpec->uCop == USEASM_INTSRCSEL_ADD &&
		psSpec->uAop == USEASM_INTSRCSEL_ADD &&
		(!psSpec->bNegCop) &&
		(!psSpec->bNegAop))
	{
		psMultSop = psSpec;
	}
	else
	{
		bMatch = IMG_FALSE;
	}

	/* Test that the destination for the add is a source in the mult */
	if (bMatch)
	{
		if (EqualRegs(IMG_TRUE, &psAddSop->sOut, &psMultSop->asSrc[1]))
			uFacIdx = 2;
		else if (EqualRegs(IMG_TRUE, &psAddSop->sOut, &psMultSop->asSrc[2]))
			uFacIdx = 1;
		else
			bMatch = IMG_FALSE;
	}

	/* Test for the match */
	if (bMatch)
	{
		PUSE_INST apsNewInst[2] = {NULL, NULL};
		IMG_UINT32 uInstFlags = 0;

		/* Form the new sop3 */
		apsSpec[1] = AllocSopSpec(psState);
		psNewSop = apsSpec[1];

		/* Opcode */
		psNewSop->uOpcode = USEASM_OP_SOP3;

		/* Format control */
		psNewSop->bFmtControl = psMultSop->bFmtControl;

		/* Destination */
		psNewSop->sOut = psMultSop->sOut;

		/* Src0 */
		psNewSop->asSrc[0] = psMultSop->asSrc[uFacIdx];

		/* Src1 */
		psNewSop->asSrc[1] = psAddSop->asSrc[1];

		/* Src2 */
		psNewSop->asSrc[2] = psAddSop->asSrc[2];

		/* Src1 Colour factor */
		psNewSop->uSrc1ColFactor = USEASM_INTSRCSEL_SRC0;

		/* Src2 Colour factor */
		psNewSop->uSrc2ColFactor = USEASM_INTSRCSEL_SRC0;

		/* Colour operation */
		psNewSop->uCop = USEASM_INTSRCSEL_ADD;

		/* Src1 Alpha factor */
		psNewSop->uSrc1AlphaFactor = USEASM_INTSRCSEL_SRC0;

		/* Src2 Alpha factor */
		psNewSop->uSrc2AlphaFactor = USEASM_INTSRCSEL_SRC0;

		/* Alpha operation */
		psNewSop->uAop = USEASM_INTSRCSEL_ADD;

		/* Make the instruction */
		apsNewInst[0] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[0]);

		apsNewInst[1] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[1]);

		if (!EncodeSopSpec(psNewSop, apsNewInst))
		{
			goto Exit;
		}
		ENSURE(psState, apsNewInst[0] != NULL);
		ENSURE(psState, apsNewInst[1] != NULL);

		/* Set the instruction flags */
		uInstFlags = GetInstFlags(psInst);
		SetInstFlags(apsNewInst[0], uInstFlags, IMG_TRUE);
		SetInstFlags(apsNewInst[1], uInstFlags, IMG_TRUE);

		/* Insert the new instruction in front of the old instruction */
		InsertBefore(psState, psBlock, apsNewInst[0], psInst);
		InsertBefore(psState, psBlock, apsNewInst[1], psInst);

		/* Remove the first instruction */
		DeleteInst(psState, psBlock, psInst);

		/* Remove the second instruction */
		DeleteInst(psState, psBlock, psNext);

		/* Finish */
		bChanged = IMG_TRUE;
	}

  Exit:
	/* Clean up */
	{
		IMG_UINT32 uCtr;
		for (uCtr = 0; uCtr < uNumSpecs; uCtr ++)
		{
			UseoptFree(psState, apsSpec[uCtr]);
		}
	}

	if (ppsNewInst != NULL)
		(*ppsNewInst) = NULL;
	return bChanged;
}

/*****************************************************************************
 FUNCTION	: Sop2MultAdd

 PURPOSE	: Reduce a sop2 multiply-add

 PARAMETERS	: psState    - Optimiser state
              psInst     - Sop2/Sop2wm instruction
              psNext     - Next instruction
              psSpec     - Specification of the instruction

 OUTPUT     : ppsNewInst - New instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL Sop2MultAdd(PUSEOPT_STATE psState,
					 PUSE_BLOCK psBlock,
					 PUSE_INST psInst,
					 PUSE_INST psNext,
					 PUSE_SOPSPEC psFirstSpec,
					 PUSE_INST *ppsNewInst)
{
	const IMG_UINT32 uNumSpecs = 2;
	IMG_BOOL bChanged = IMG_FALSE;
	IMG_BOOL bMatch = IMG_FALSE;
	IMG_UINT32 uFacIdx =  USE_UNDEF;
	PUSE_SOPSPEC psNextSpec = NULL;
	PUSE_SOPSPEC psAddSop = NULL;
	PUSE_SOPSPEC psMultSop = NULL;
	PUSE_SOPSPEC psNewSop = NULL;
	PUSE_SOPSPEC psSpec = NULL;
	PUSE_INST psNext1 = NULL;
	PUSE_INST psNewInst = NULL;
	PUSE_SOPSPEC apsSpec[2] = {NULL, NULL};

	/*
	 * Combine integer mult and add (from libcombiner).
	 *
	 * mult: sop2 z, x, y, src2, zero, +, src2, zero, +
     *       (z = [x * y])
	 * add:  sop2 d, z, a, one, one, +, one, one, +
	 *       (d = [z + a])
	 * -->
	 * mad:  sop3 d, x, y, a, src0, one, +, src0, +
	 *       (d = [(x * y) + a])
	 */

	bMatch = IMG_TRUE;

	/* Test the first sop for being an add */
    psSpec = psFirstSpec;
    ENSURE(psState,
		   psSpec->uOpcode == USEASM_OP_SOP2 ||
		   psSpec->uOpcode == USEASM_OP_SOP2WM);

	/* Get the next sop */
	if (bMatch && psNext == NULL)
	{
		bMatch = IMG_FALSE;
	}

	apsSpec[0] = AllocSopSpec(psState);
	if (bMatch && !ExtractSop2Spec(psNext, apsSpec[0], &psNext1))
	{
		bMatch = IMG_FALSE;
	}
	psNextSpec = apsSpec[0];

	/* Test the first sop for multiplication */
	psSpec = psFirstSpec;
	if (bMatch &&
		psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_SRC2 &&
		psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ZERO &&
		psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_SRC2ALPHA &&
		psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ZERO &&
		psSpec->uCop == USEASM_INTSRCSEL_ADD &&
		psSpec->uAop == USEASM_INTSRCSEL_ADD &&
		(!psSpec->bNegCop) &&
		(!psSpec->bNegAop))
	{
		psMultSop = psSpec;
	}
	else
	{
		bMatch = IMG_FALSE;
	}

	/* Test the second sop for addition */
	psSpec = psNextSpec;
	if (bMatch &&
		psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uCop == USEASM_INTSRCSEL_ADD &&
		psSpec->uAop == USEASM_INTSRCSEL_ADD &&
		(!psSpec->bNegCop) &&
		(!psSpec->bNegAop))
	{
		psAddSop = psSpec;
	}
	else
	{
		bMatch = IMG_FALSE;
	}

	/* Test that the destination for the mult is a source in the add */
	if (bMatch)
	{
		if (EqualRegs(IMG_TRUE, &psMultSop->sOut, &psAddSop->asSrc[1]))
			uFacIdx = 2;
		else if (EqualRegs(IMG_TRUE, &psMultSop->sOut, &psAddSop->asSrc[2]))
			uFacIdx = 1;
		else
			bMatch = IMG_FALSE;
	}

	/* Test for the match */
	if (bMatch)
	{
		PUSE_INST apsNewInst[2] = {NULL, NULL};
		IMG_UINT32 uInstFlags = 0;

		/* Form the new sop3 */
		apsSpec[1] = AllocSopSpec(psState);
		psNewSop = apsSpec[1];

		/* Opcode */
		psNewSop->uOpcode = USEASM_OP_SOP3;

		/* Format control */
		psNewSop->bFmtControl = psMultSop->bFmtControl;

		/* Destination */
		psNewSop->sOut = psNextSpec->sOut;

		/* Src0 */
		psNewSop->asSrc[0] = psMultSop->asSrc[1];

		/* Src1 */
		psNewSop->asSrc[1] = psMultSop->asSrc[2];

		/* Src2 */
		psNewSop->asSrc[2] = psAddSop->asSrc[uFacIdx];

		/* Src1 Colour factor */
		psNewSop->uSrc1ColFactor = USEASM_INTSRCSEL_SRC0;

		/* Src2 Colour factor */
		psNewSop->uSrc2ColFactor = USEASM_INTSRCSEL_ONE;

		/* Colour operation */
		psNewSop->uCop = USEASM_INTSRCSEL_ADD;

		/* Src1 Alpha factor */
		psNewSop->uSrc1AlphaFactor = USEASM_INTSRCSEL_SRC0;

		/* Src2 Alpha factor */
		psNewSop->uSrc2AlphaFactor = USEASM_INTSRCSEL_ONE;

		/* Alpha operation */
		psNewSop->uAop = USEASM_INTSRCSEL_ADD;

		/* Make the instruction */
		apsNewInst[0] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[0]);

		apsNewInst[1] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[1]);

		if (!EncodeSopSpec(psNewSop, apsNewInst))
		{
			goto Exit;
		}
		ENSURE(psState, apsNewInst[0] != NULL);
		ENSURE(psState, apsNewInst[1] != NULL);

		/* Set the instruction flags */
		uInstFlags = GetInstFlags(psNext);
		SetInstFlags(apsNewInst[0], uInstFlags, IMG_TRUE);
		SetInstFlags(apsNewInst[1], uInstFlags, IMG_TRUE);

		/* Insert the new instruction in front of the old instruction */
		InsertBefore(psState, psBlock, apsNewInst[0], psInst);
		InsertBefore(psState, psBlock, apsNewInst[1], psInst);

		/* Remove the first instruction */
		DeleteInst(psState, psBlock, psInst);

		/* Remove the second instruction */
		DeleteInst(psState, psBlock, psNext);

		/* Set the new instruction */
		psNewInst = apsNewInst[0];

		/* Finish */
		bChanged = IMG_TRUE;
	}

  Exit:
	/* Clean up */
	{
		IMG_UINT32 uCtr;
		for (uCtr = 0; uCtr < uNumSpecs; uCtr ++)
		{
			UseoptFree(psState, apsSpec[uCtr]);
		}
	}

	if (ppsNewInst != NULL)
		(*ppsNewInst) = psNewInst;
	return bChanged;
}

/*****************************************************************************
 FUNCTION	: ElimUnusedAlpha

 PURPOSE	: Elminate an unused alpha

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Code block
              psInst     - Sop2/Sop2wm instruction
              psNext     - Next instruction
              psSpec     - Specification of the instruction

 OUTPUT     : ppsNewInst - New instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL ElimUnusedAlpha(PUSEOPT_STATE psState,
						 PUSE_BLOCK psBlock,
						 PUSE_INST psInst,
						 PUSE_INST psNext,
						 PUSE_SOPSPEC psSopSpec,
						 PUSE_INST *ppsNewInst)
{
	const IMG_UINT32 uNumSpecs = 2;
	IMG_BOOL bChanged = IMG_FALSE;
	IMG_BOOL bMatch = IMG_TRUE;
	IMG_BOOL bDeleteInst = IMG_TRUE;
	IMG_UINT32 uFacIdx =  USE_UNDEF;
	IMG_UINT32 uOtherIdx =  USE_UNDEF;
	PUSE_SOPSPEC psNextSop;
	PUSE_SOPSPEC psNewSop = NULL;
	PUSE_SOPSPEC psSpec = NULL;
	PUSE_INST psNext1 = NULL;
	PUSE_SOPSPEC apsSpec[2] = {NULL, NULL};

	/*
	 * Eliminate unused inst alpha setting
	 *
	 * sop :  sop2 d, r0, r1, one, zero, +, zero, one, +
	 *        (d = [r0.rgb,  r1.a])
	 * next: sop2 d, d, r3, one, zero, +, zero, one, +
     *        (d = [ r0.rgb, r3.a ])
	 * -->
	 * new :  sop2 d, r0, r3, one, zero, +, zero, one, +
	 *        (d = [ r0.rgb, r3.a ])
	 *
	 *
	 * sop :  sop2 d, r0, r1, one, zero, +, zero, one, +
	 *        (d = [r0.rgb,  r1.a])
	 * next: sop2 d, r3, d, one, zero, +, one, zero, +
     *        (d = [ r0.rgb, r3.a ])
	 * -->
	 * new :  sop2 d, r0, r3, one, zero, +, zero, one, +
	 *        (d = [ r0.rgb, r3.a ])
	 *
	 */

	/* Get the next sop */
	if (bMatch && psNext == NULL)
	{
		bMatch = IMG_FALSE;
	}

	apsSpec[0] = AllocSopSpec(psState);
	if (bMatch && !ExtractSop2Spec(psNext, apsSpec[0], &psNext1))
	{
		bMatch = IMG_FALSE;
	}
	psNextSop = apsSpec[0];

	/* Test that the destinations for both sops are the same */
	if (bMatch &&
		!EqualRegs(IMG_TRUE, &psSopSpec->sOut, &psNextSop->sOut))
	{
		bDeleteInst = IMG_FALSE;
	}

	/* Test the first sop */
    psSpec = psSopSpec;
    ENSURE(psState,
		   psSpec->uOpcode == USEASM_OP_SOP2 ||
		   psSpec->uOpcode == USEASM_OP_SOP2WM);

	if (bMatch &&
		psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ZERO &&
		psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_ZERO &&
		psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ONE &&
		psSpec->uCop == USEASM_INTSRCSEL_ADD &&
		psSpec->uAop == USEASM_INTSRCSEL_ADD &&
		(!psSpec->bNegCop) &&
		(!psSpec->bNegAop))
	{
		bMatch = IMG_TRUE;
	}
	else
	{
		bMatch = IMG_FALSE;
	}

	if (!bMatch)
	{
		/* No match, end early */
		goto Exit;
	}

	/* Test that the destination for the first is a source in the second */
	if (bMatch)
	{
		if (EqualRegs(IMG_TRUE, &psSopSpec->sOut, &psNextSop->asSrc[1]))
		{
			uFacIdx = 1;
			uOtherIdx = 2;
		}
		else if (EqualRegs(IMG_TRUE, &psSopSpec->sOut, &psNextSop->asSrc[2]))
		{
			uFacIdx = 2;
			uOtherIdx = 1;
		}
		else
			bMatch = IMG_FALSE;
	}

	/* Test the destination mask of the second sop */
	if (((psSopSpec->uWriteMask & USE_RGB_CHAN_MASK) == 0) ||
		((psNextSop->uWriteMask & USE_ALPHA_CHAN_MASK) == 0))
	{
		/*
		  First sop doesn't write the rgb or second sop doesn't write
		  to the alpha
		*/
		bMatch = IMG_FALSE;
	}

	if (!bMatch)
	{
		/* No match, end early */
		goto Exit;
	}

	/* Test the second sop for using the rgb-apha part of the first */
	psSpec = psNextSop;
	if (bMatch &&
		((uFacIdx == 1 &&
		  psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_ONE &&
		  psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ZERO &&
		  psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_ZERO &&
		  psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ONE)
		 ||
		 (uFacIdx == 2 &&
		  psSpec->uSrc1ColFactor == USEASM_INTSRCSEL_ZERO &&
		  psSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ONE &&
		  psSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_ONE &&
		  psSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ZERO))
		&&
		psSpec->uCop == USEASM_INTSRCSEL_ADD &&
		psSpec->uAop == USEASM_INTSRCSEL_ADD &&
		(!psSpec->bNegCop) &&
		(!psSpec->bNegAop))
	{
		bMatch = IMG_TRUE;
	}
	else
	{
		bMatch = IMG_FALSE;
	}

	if (!bMatch)
	{
		/* No match, end early */
		goto Exit;
	}

	/* Form the new instruction */
	if (bMatch)
	{
		PUSE_INST apsNewInst[2] = {NULL, NULL};
		IMG_UINT32 uInstFlags = 0;

		/* Form the new sop3 */
		apsSpec[1] = AllocSopSpec(psState);
		psNewSop = apsSpec[1];

		/* Opcode */
		psNewSop->uOpcode = USEASM_OP_SOP2;

		/* Format control */
		psNewSop->bFmtControl = psNextSop->bFmtControl;

		/* Destination */
		psNewSop->sOut = psNextSop->sOut;

		/* Src1 */
		psNewSop->asSrc[1] = psSopSpec->asSrc[1];

		/* Src2 */
		psNewSop->asSrc[2] = psNextSop->asSrc[uOtherIdx];

		/* Src1 Colour factor */
		psNewSop->uSrc1ColFactor = USEASM_INTSRCSEL_ONE;

		/* Src2 Colour factor */
		psNewSop->uSrc2ColFactor = USEASM_INTSRCSEL_ZERO;

		/* Colour operation */
		psNewSop->uCop = USEASM_INTSRCSEL_ADD;

		/* Src1 Alpha factor */
		psNewSop->uSrc1AlphaFactor = USEASM_INTSRCSEL_ZERO;

		/* Src2 Alpha factor */
		psNewSop->uSrc2AlphaFactor = USEASM_INTSRCSEL_ONE;

		/* Alpha operation */
		psNewSop->uAop = USEASM_INTSRCSEL_ADD;

		/* Make the instruction */
		apsNewInst[0] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[0]);

		apsNewInst[1] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[1]);

		if (!EncodeSopSpec(psNewSop, apsNewInst))
		{
			goto Exit;
		}
		ENSURE(psState, apsNewInst[0] != NULL);
		ENSURE(psState, apsNewInst[1] != NULL);

		/* Set the instruction flags */
		uInstFlags = GetInstFlags(psNext);
		SetInstFlags(apsNewInst[0], uInstFlags, IMG_TRUE);
		SetInstFlags(apsNewInst[1], uInstFlags, IMG_TRUE);

		/* Insert the new instruction in front of the old instruction */
		InsertBefore(psState, psBlock, apsNewInst[0], psNext);
		InsertBefore(psState, psBlock, apsNewInst[1], psNext);

		/* Remove the first instruction */
		if (bDeleteInst)
		{
			DeleteInst(psState, psBlock, psInst);
		}

		/* Remove the second instruction */
		DeleteInst(psState, psBlock, psNext);

		/* Finish */
		bChanged = IMG_TRUE;
	}

  Exit:
	/* Clean up */
	{
		IMG_UINT32 uCtr;
		for (uCtr = 0; uCtr < uNumSpecs; uCtr ++)
		{
			UseoptFree(psState, apsSpec[uCtr]);
		}
	}
	if (ppsNewInst != NULL)
		(*ppsNewInst) = NULL;
	return bChanged;
}

/*****************************************************************************
 FUNCTION	: IsScaleSop

 PURPOSE	: Test whether a sop is used to apply scaling

 PARAMETERS	: psState      - Optimiser state
              psInst       - Instruction to test

 OUTPUT     : pbWriteRGB   - Whether RGB is written
              pbWriteAlpha - Whether alpha is written
              puCScale     - Colour scaling value
              puAScale     - Alpha scaling value

 RETURNS	: IMG_TRUE iff instruction is scaling
*****************************************************************************/
static
IMG_BOOL IsScalingSop(PUSEOPT_STATE psState,
					  PUSE_INST psInst,
					  IMG_PBOOL pbWriteRGB,
					  IMG_PBOOL pbWriteAlpha,
					  IMG_PUINT32 puCScale,
					  IMG_PUINT32 puAScale)
{
	IMG_BOOL bWriteRGB = IMG_FALSE, bWriteAlpha = IMG_FALSE;
	IMG_UINT32 uCScale = 0, uAScale = 0;
	IMG_UINT32 uScaleConst = 0;
	const IMG_UINT32 uDestArg = 0;
	const IMG_UINT32 uRegArg = 1;
	const IMG_UINT32 uScaleArg = 2;
	const IMG_UINT32 uS1CFactArg = 3;
	const IMG_UINT32 uS2CFactArg = 4;
	const IMG_UINT32 uCopArg = 5;
	const IMG_UINT32 uAopArg = 6;

	PVR_UNREFERENCED_PARAMETER(psState);

	/*
	  An instruction applies scaling iff it is of the form

	  sop2wm r.msk, r, #scale, s2, 0, +, +

	  where r is the register to scale and msk is one of
	  USE_RGB_CHAN_MASK, USE_ALPHA_CHAN_MASK or USE_ALL_CHAN_MASK
	 */

	if (psInst == NULL)
	{
		return IMG_FALSE;
	}

	/* Test opcode */
	if (psInst->uOpcode != USEASM_OP_SOP2WM)
	{
		return IMG_FALSE;
	}
	/* Check write mask */
	{
		IMG_UINT32 uWriteMask = GetWriteMask(psInst);
		IMG_UINT32 uRGBPart = (uWriteMask & USE_RGB_CHAN_MASK);

		if (uWriteMask == 0)
		{
			/* Invalid write mask */
			return IMG_FALSE;
		}

		bWriteRGB = IMG_FALSE;
		if (uRGBPart == USE_RGB_CHAN_MASK)
		{
			bWriteRGB = IMG_TRUE;
		}
		else if (uRGBPart != 0)
		{
			/* Invalid write mask */
			return IMG_FALSE;
		}

		bWriteAlpha = IMG_FALSE;
		if (uWriteMask & USE_ALPHA_CHAN_MASK)
		{
			bWriteAlpha = IMG_TRUE;
		}
	}
	/* Check scaling constant */
	{
		PUSE_REGISTER psReg = &(psInst->asArg[uScaleArg]);

		if (psReg->uType != USEASM_REGTYPE_IMMEDIATE)
		{
			/* Not a scaling constant */
			return IMG_FALSE;
		}
		uScaleConst = psReg->uNumber;
	}

	/* Check registers */
	{
		PUSE_REGISTER psDest = &(psInst->asArg[uDestArg]);
		PUSE_REGISTER psReg = &(psInst->asArg[uRegArg]);

		if (!EqualRegs(IMG_TRUE, psDest, psReg))
		{
			/* Invalid registers */
			return IMG_FALSE;
		}
	}

	/* Check Src1 RGB factors */
	if (psInst->asArg[uS1CFactArg].uNumber != USEASM_INTSRCSEL_SRC2 ||
		(psInst->asArg[uS1CFactArg].uFlags & USEASM_ARGFLAGS_COMPLEMENT))
	{
		return IMG_FALSE;
	}
	/* Check Src2 RGB Factors */
	if (psInst->asArg[uS2CFactArg].uNumber != USEASM_INTSRCSEL_ZERO ||
		(psInst->asArg[uS2CFactArg].uFlags & USEASM_ARGFLAGS_COMPLEMENT))
	{
		return IMG_FALSE;
	}

	/* Check Operations */
	if (psInst->asArg[uCopArg].uNumber != USEASM_INTSRCSEL_ADD ||
		psInst->asArg[uAopArg].uNumber != USEASM_INTSRCSEL_ADD)
	{
		return IMG_FALSE;
	}

	/* Set the scaling */
	if (bWriteRGB)
		uCScale = uScaleConst;
	else
		uCScale = 0;

	if (bWriteAlpha)
		uAScale = uScaleConst;
	else
		uAScale = 0;

	/* Write the results */
	if (pbWriteRGB != NULL)
		(*pbWriteRGB) = bWriteRGB;
	if (pbWriteAlpha != NULL)
		(*pbWriteAlpha) = bWriteAlpha;

	if (puCScale)
		(*puCScale) = uCScale;
	if (puAScale)
		(*puAScale) = uAScale;

	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: Sop2Scaling

 PURPOSE	: Reduce a sop2 scaling

 PARAMETERS	: psState    - Optimiser state
              psInst     - Sop2/Sop2wm instruction
              psNext     - Next instruction
              psSpec     - Specification of the instruction

 OUTPUT     : ppsNewInst - New instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL Sop2Scaling(PUSEOPT_STATE psState,
					 PUSE_BLOCK psBlock,
					 PUSE_INST psInst,
					 PUSE_INST psNext,
					 PUSE_SOPSPEC psFirstSpec,
					 PUSE_INST *ppsNewInst)
{
	const IMG_UINT32 uS1ColFactorArg = 4;
	const IMG_UINT32 uS1AlphaFactorArg = 1;
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_SOPSPEC psNextSpec = NULL;
	PUSE_INST psCoInst = NULL;
	IMG_BOOL bWriteRGB, bWriteAlpha;
	IMG_UINT32 uCScale, uAScale;
	/*
	 * Eliminate unused inst alpha setting
	 *
	 * sop :  sop2 d, r0, r1, src2, _, +, src2, _, +
	 * next:  sop2 d, d, #2, src2, zero, +, src2, zero, +
	 * -->
	 * new :  sop2 d, r0, r1, src2scale, _, +, src2scale, _, +
	 *
	 * sop :  sop2  d, r0, r1, src2, _, +, src2, _, +
	 * next:  sopwm d, d, #2, src2, zero, +, src2, zero, +
	 * -->
	 * new :  sop2 d, r0, r1, src2scale, _, +, src2scale, _, +
	 */

	/* Check for valid first instruction */
	if (psInst->uOpcode != USEASM_OP_SOP2  ||
		(psFirstSpec->uSrc1ColFactor != USEASM_INTSRCSEL_SRC2 &&
		 psFirstSpec->uSrc1AlphaFactor != USEASM_INTSRCSEL_SRC2ALPHA))
	{
		goto Exit;
	}
	psCoInst = psInst->psNext;
	if (psCoInst == NULL)
	{
		goto Exit;
	}
	if (psCoInst->uOpcode != USEASM_OP_ASOP2)
	{
		goto Exit;
	}

	/* Check for scaling */
	bWriteRGB = IMG_FALSE;
	bWriteAlpha = IMG_FALSE;
	if (!IsScalingSop(psState, psNext,
					  &bWriteRGB, &bWriteAlpha,
					  &uCScale, &uAScale))
	{
		goto Exit;
	}

	/* Check scaling is valid */
	if (bWriteRGB && uCScale != 2)
	{
		goto Exit;
	}
	if (bWriteAlpha && uAScale != 2)
	{
		goto Exit;
	}

    /* Update instruction */
	if (bWriteRGB && uCScale == 2)
	{
		psInst->asArg[uS1ColFactorArg].uNumber = USEASM_INTSRCSEL_SRC2SCALE;
		bChanged = IMG_TRUE;
	}
	if (uAScale == 2)
	{
		psCoInst->asArg[uS1AlphaFactorArg].uNumber = USEASM_INTSRCSEL_SRC2SCALE;
		bChanged = IMG_TRUE;
	}

	/* Remove the scaling instruction */
	if (bChanged)
	{
		DeleteInst(psState, psBlock, psNext);
	}

	if (ppsNewInst != NULL)
	{
		(*ppsNewInst) = psInst;
	}


  Exit:
	/* Clean up */
	{
		UseoptFree(psState, psNextSpec);
	}

	return bChanged;
}

#if 0
/*****************************************************************************
 FUNCTION	: Sop2C10Conv

 PURPOSE	: Reduce a sop2 c10 data conversion

 PARAMETERS	: psState    - Optimiser state
              psInst     - Sop2/Sop2wm instruction
              psNext     - Next instruction
              psSpec     - Specification of the instruction

 OUTPUT     : ppsNewInst - New instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL Sop2C10Conv(PUSEOPT_STATE psState,
					 PUSE_BLOCK psBlock,
					 PUSE_INST psInst,
					 PUSE_INST psNext,
					 PUSE_SOPSPEC psFirstSpec,
					 PUSE_INST *ppsNewInst)
{
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_SOPSPEC psNextSpec = NULL;
	PUSE_INST psCoInst = NULL;
	PUSE_INST psNextInst = NULL;
	PUSE_REGISTER psNextSrc = NULL;

	/*
	 * Combine a sop2 used as a data conversion
	 *
	 * sop :  sop2 d, d.c10, _, one, zero, +, one, zero, +
	 * next:  sop2 d, d, x, y, z, a, b, c, +
	 * -->
	 * new :  sop2 d, r.c10, x, y, z, a, b, c
	 *
	 */

	/* Check for valid first instruction */
	if (!((psInst->uOpcode == USEASM_OP_SOP2 || psInst->uOpcode == USEASM_OP_SOP2WM) &&
		  psFirstSpec->uSrc1ColFactor == USEASM_INTSRCSEL_ONE &&
		  psFirstSpec->uSrc1AlphaFactor == USEASM_INTSRCSEL_ONE &&
		  psFirstSpec->uSrc2ColFactor == USEASM_INTSRCSEL_ZERO &&
		  psFirstSpec->uSrc2AlphaFactor == USEASM_INTSRCSEL_ZERO))
	{
		goto Exit;
	}
	psCoInst = psInst->psNext;
	if (psCoInst == NULL)
	{
		goto Exit;
	}
	if (psCoInst->uOpcode != USEASM_OP_ASOP2)
	{
		goto Exit;
	}

	/* Get next sop */
	psNextSpec = AllocSopSpec(psState);
	if (!ExtractSop2Spec(psNext, psNextSpec, &psNextInst))
	{
		goto Exit;
	}

	/* Check next sop */
	if (!((psInst->uOpcode == USEASM_OP_SOP2 || psInst->uOpcode == USEASM_OP_SOP2WM)))
	{
		goto Exit;
	}

	/* Check for conversion of src1 */
	{
		PUSE_REGISTER psDst = &(psFirstSpec->sOut);
		PUSE_REGISTER psSrc = &(psFirstSpec->asSrc[1]);
		PUSE_REGISTER psNextDst = &(psNextSpec->sOut);

		if (!EqualRegs(IMG_TRUE, psDst, psSrc))
		{
			goto Exit;
		}
		if (psDst->uFlags & USEASM_ARGFLAGS_FMTC10)
		{
			goto Exit;
		}
		if (!(psSrc->uFlags & USEASM_ARGFLAGS_FMTC10))
		{
			goto Exit;
		}

		/* Match destinations */
		if (!(EqualRegs(IMG_TRUE, psNextDst, psDst)))
		{
			goto Exit;
		}

		/* Match a source in the next sop */
		psNextSrc = &(psNextSpec->asSrc[0]);
		if (!EqualRegs(IMG_TRUE, psDst, psNextSrc))
		{
			psNextSrc = &(psNextSpec->asSrc[1]);
			if (!EqualRegs(IMG_TRUE, psDst, psNextSrc))
			{
				/* No match */
				goto Exit;
			}
		}
		if (psNextSrc->uFlags & USEASM_ARGFLAGS_FMTC10)
		{
			goto Exit;
		}
	}
	/*
	 * Got a match
	 */

	{	
		PUSE_INST apsNewInst[2] = {NULL, NULL};
		IMG_UINT32 uInstFlags = 0;

		/* Set the c10 flag on the register use */
		psNextSrc->uFlags |= USEASM_ARGFLAGS_FMTC10;
		if (psInst->uFlags2 & USEASM_OPFLAGS2_FORMATSELECT)
		{
			psNext->uFlags2 |= USEASM_OPFLAGS2_FORMATSELECT;
		}

		/* Make the instruction */
		apsNewInst[0] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[0]);

		apsNewInst[1] = UseoptAlloc(psState, sizeof(USE_INST));
		UseAsmInitInst(apsNewInst[1]);

		if (!EncodeSopSpec(psNextSpec, apsNewInst))
		{
			goto Exit;
		}
		ENSURE(psState, apsNewInst[0] != NULL);
		ENSURE(psState, apsNewInst[1] != NULL);
		/* Set the instruction flags */
		uInstFlags = GetInstFlags(psNext);
		SetInstFlags(apsNewInst[0], uInstFlags, IMG_TRUE);
		SetInstFlags(apsNewInst[1], uInstFlags, IMG_TRUE);

		/* Insert the new instruction in front of the old instruction */
		InsertBefore(psState, psBlock, apsNewInst[0], psInst);
		InsertBefore(psState, psBlock, apsNewInst[1], psInst);

		/* Remove the first instruction */
		DeleteInst(psState, psBlock, psInst);

		/* Remove the second instruction */
		DeleteInst(psState, psBlock, psNext);

		/* Set the new instruction */
		psNext = apsNewInst[0];

		/* Finish */
		bChanged = IMG_TRUE;
	}

	if (ppsNewInst != NULL)
	{
		(*ppsNewInst) = psNext;
	}

  Exit:
	/* Clean up */
	{
		UseoptFree(psState, psNextSpec);
	}

	return bChanged;
}
#endif

/*****************************************************************************
 FUNCTION	: PeepHoleSop2

 PURPOSE	: Apply peephole optimisations to sop2 sop2m instructions

 PARAMETERS	: psState    - Optimiser state
              psInst     - Sop2/Sop2wm instruction
              bProgEnd   - Whether it is the final instructions

 OUTPUT     : ppsNewInst - New instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL PeepHoleSop2(PUSEOPT_STATE psState,
					  PUSE_BLOCK psBlock,
					  PUSE_INST psInst,
					  PUSE_INST *ppsNewInst)
{
	PUSE_INST psNext = NULL;
	IMG_BOOL bFailed = IMG_FALSE;
	USEASM_OPCODE uOpcode = psInst->uOpcode;
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_SOPSPEC psSopSpec = NULL;

	ENSURE(psState, psBlock != NULL);

	uOpcode = psInst->uOpcode;

	/* Get the first sop */
	if (uOpcode != USEASM_OP_SOP2 &&
		uOpcode != USEASM_OP_SOP2WM)
	{
		goto Exit;
	}

	/* Test for a sop being used as a move */
	{
		PUSE_REGISTER psDst = NULL, psSrc = NULL;
		IMG_UINT32 uFlags = 0;
		PUSE_INST psPrevInst, psNextInst;
		USEASM_REGTYPE uDstType, uSrcType;
		IMG_UINT32 uDstNum, uSrcNum;

		if (IsMove(psInst, &psDst, &psSrc))
		{
			/* Replace with a mov */
			if (uOpcode == USEASM_OP_SOP2)
			{
				/* Remove the co-instruction */
				psNext = psInst->psNext;
				if (psNext == NULL)
				{
					goto Exit;
				}

				DeleteInst(psState, psBlock, psNext);
			}

			/* Set the main instruction */
			SetRepeatMask(psInst, 0);
			uFlags = GetInstFlags(psInst);

			psPrevInst = psInst->psPrev;
			psNextInst = psInst->psNext;

			uDstType = psDst->uType;
			uDstNum = psDst->uNumber;

			uSrcType = psSrc->uType;
			uSrcNum = psSrc->uNumber;

			UseAsmInitInst(psInst);
			psInst->uOpcode = USEASM_OP_MOV;

			UseoptInitReg(&(psInst->asArg[0]));
			psInst->asArg[0].uType = uDstType;
			psInst->asArg[0].uNumber = uDstNum;

			UseoptInitReg(&(psInst->asArg[1]));
			psInst->asArg[1].uType = uSrcType;
			psInst->asArg[1].uNumber = uSrcNum;

			SetInstFlags(psInst, uFlags, IMG_TRUE);
			SetRepeatCount(psInst, 1);

			psInst->psNext = psNextInst;
			psInst->psPrev = psPrevInst;

			/* Done */
			bChanged = IMG_TRUE;
			goto Exit;
		}
	}

	/* Get the specification */
	psSopSpec = AllocSopSpec(psState);
	if (!ExtractSop2Spec(psInst, psSopSpec, &psNext))
	{
		bFailed = IMG_TRUE;
		goto Exit;
	}

	/* Test for no following instruction */
	if (psNext == NULL)
	{
		/* Nothing to combine with */
		bChanged = IMG_FALSE;
		goto Exit;
	}

	/* Try sop2-sop2 transforms */
	if (Sop2AddMult(psState, psBlock, psInst, psNext, psSopSpec, ppsNewInst))
	{
		bChanged = IMG_TRUE;
		goto Exit;
	}
	if (Sop2MultAdd(psState, psBlock, psInst, psNext, psSopSpec, ppsNewInst))
	{
		bChanged = IMG_TRUE;
		goto Exit;
	}
	if (ElimUnusedAlpha(psState, psBlock, psInst, psNext, psSopSpec, ppsNewInst))
	{
		bChanged = IMG_TRUE;
		goto Exit;
	}
	if (Sop2Scaling(psState, psBlock, psInst, psNext, psSopSpec, ppsNewInst))
	{
		bChanged = IMG_TRUE;
		goto Exit;
	}
#if 0
	if (Sop2C10Conv(psState, psBlock, psInst, psNext, psSopSpec, ppsNewInst))
	{
		bChanged = IMG_TRUE;
		goto Exit;
	}
#endif

  Exit:
	UseoptFree(psState, psSopSpec);

	if (bFailed)
	{
		return IMG_FALSE;
	}

	return bChanged;
}


/*****************************************************************************
 FUNCTION	: PeepHole

 PURPOSE	: Apply peephole optimisations at an instruction

 PARAMETERS	: psState     - Optimiser state
              psBlock     - Code block
              psInst      - First new instruction

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL PeepHole(PUSEOPT_STATE psState,
				  PUSE_BLOCK psBlock,
				  PUSE_INST psInst,
				  PUSE_INST *ppsNewInst)
{
	IMG_BOOL bChange = IMG_FALSE;
	PUSE_INST psNewInst = NULL;

	/*
	 * Notation
	 * --------
	 * EOP: End of program
	 * inst r.rgba, ... : instruction writes to all channels of r
	 */

	/*
	 * Get some properties of psInst
	 */

	if (psInst == NULL)
	{
		goto Exit;
	}
	psNewInst = NULL;

	/* Test for instruction which are not handled */
	if (GetPredicate(psInst) != USEASM_PRED_NONE ||
		GetRepeatCount(psInst) > 1)
	{
		goto Exit;
	}

    /* Switch by opcode */
    switch(psInst->uOpcode)
    {
		case USEASM_OP_SOP2:
		case USEASM_OP_SOP2WM:
		{
			bChange = PeepHoleSop2(psState, psBlock, psInst, &psNewInst);
            break;
		}
        default:
		{
			bChange = IMG_FALSE;
            break;
		}
    }

  Exit:
	if (bChange)
	{
		*ppsNewInst = psNewInst;
	}
	return bChange;
}


/*****************************************************************************
 FUNCTION	: PeepHoleBlock

 PURPOSE	: Apply peephole optimisation to a block of code

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Block to optimise

 RETURNS	: IMG_TRUE iff an optimisation was applied
*****************************************************************************/
static
IMG_BOOL PeepHoleBlock(PUSEOPT_STATE psState,
					   PUSE_BLOCK psBlock)
{
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_INST psNext = NULL;
	PUSE_INST psCurr = NULL;

	/*
	  Iterate through the current block applying a peephole optimiser
	*/
	psNext = NULL;

    for (psCurr = psBlock->psFirst;
		 psCurr != NULL;
		 psCurr = psNext)
	{
		PUSE_INST psTmp = NULL;

		psNext = GetNextInst(psCurr);

		/* Test for instructions which aren't handled */
		if (IsLabel(psCurr))
		{
			continue;
		}

		if (IsBlockEnd(psCurr))
		{
			break;
        }

        /* Apply peephole optimisations */
		if (PeepHole(psState, psBlock, psCurr, &psTmp))
        {
            bChanged = IMG_TRUE;
			psNext = psTmp;
		}
	}

	return bChanged;
}

/*****************************************************************************
 FUNCTION	: EliminateMoves

 PURPOSE	: Try to remove mov instructions.

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Code block
              psInst     - Instruction to process

 RETURNS	: IMG_TRUE if any move is eliminated
*****************************************************************************/
static
IMG_BOOL EliminateMoves(PUSEOPT_STATE psState,
						PUSE_BLOCK psBlock,
						PUSE_INST psInst,
						PUSE_INST *ppsNextInst)
{
	IMG_BOOL bChange = IMG_FALSE;
	IMG_BOOL bFullWrite = IMG_FALSE;
	PUSE_INST psNextInst = NULL;
	PUSE_REGISTER psDst = NULL, psSrc = NULL;
	IMG_BOOL bMatch = IMG_FALSE;
	
	/*
	 * Get some properties of psInst
	 */
	bChange = IMG_FALSE;

	if (psInst == NULL)
	{
		goto Exit;
	}

	bFullWrite = IsFullWrite(psInst);
	psNextInst = GetNextInst(psInst);

	/* Apply source replacements */
	if(!SubstInstSrc(psState, psBlock, psInst))
	{
		goto Exit;
	}

	/* Test for register being clobbered */
	if (!UpdateNodeRepl(psState, psBlock, psInst))
	{
		goto Exit;
	}

	/* Test for instructions which are not handled */
	if (GetPredicate(psInst) != USEASM_PRED_NONE)
	{
		goto Exit;
	}

	if (GetRepeatCount(psInst) > 1)
	{
		goto Exit;
	}

	/*
	     inst r.rgba, src, ..
	     mov dst, r
		 EOP
		 -->
		 inst dst.rgba, src, ..
		 EOP

		 (r must not be an output for the program)
	 */
	bMatch = IMG_FALSE;

	/* Match instructions */
	if (bFullWrite &&
		!IsMove(psInst, &psDst, &psSrc) &&
		!IsTest(psInst) &&
		IsNode(psState, &psInst->asArg[0]) &&
		psNextInst != NULL)
	{
		PUSE_REGISTER psMovDst = NULL, psMovSrc = NULL;

		if (IsMove(psNextInst, &psMovDst, &psMovSrc))
		{
			if (EqualRegs(IMG_TRUE, psMovSrc, &(psInst->asArg[0])))
			{
				bMatch = IMG_TRUE;
			}
		}
	}
	
	if (bMatch)
	{
		IMG_BOOL bIgnoreRegs;
		USE_REGISTER sReplReg;
		PUSE_INST psTmp;
		IMG_BOOL bMovChange = IMG_FALSE;
		IMG_BOOL bProgEnd = IMG_FALSE;

		/* Eliminate any moves follwing this instruction */
		bIgnoreRegs = UseoptGetBit(psState->auFlags, OPTFLAG_IGNORE_OUTREG);
		UseoptSetBit(psState->auFlags, OPTFLAG_IGNORE_OUTREG, IMG_TRUE);

		UseoptInitReg(&sReplReg);
		psTmp = NULL;
		psDst = &psInst->asArg[0];
		bMovChange = MunchMovs(psState, psBlock,
							   psNextInst,
							   psDst,
							   &sReplReg,
							   &bProgEnd,
							   &psTmp);
		if (bMovChange)
		{
			bChange = IMG_TRUE;
			bMatch = IMG_TRUE;

			psNextInst = GetNextInst(psInst);

			/* Set the program end bit */
			if (bProgEnd)
			{
				if (psNextInst == NULL)
				{
					/* Set the end flag on the current instruction */
					SetInstFlags(psInst, (1 << USE_PROGEND), bProgEnd);
				}
				else
				{
					/* Find the last instruction */
					PUSE_INST psLast = psInst;
					for (psTmp = GetNextInst(psInst);
						 psTmp != NULL;
						 psTmp = GetNextInst(psTmp))
					{
						psLast = psTmp;
					}
					SetInstFlags(psLast, (1 << USE_PROGEND), bProgEnd);
				}
			}
		}
		UseoptSetBit(psState->auFlags, OPTFLAG_IGNORE_OUTREG, bIgnoreRegs);

		/*
		  Test whether the destination of the current instruction has
		  a replacement.

		  Note that if there is a replacment, it must have come from
		  MunchMov since the destination node would be clobbered by
		  UpdateNodeRepl at the start of the function.
		*/
		if (!(sReplReg.uType < USEASM_REGTYPE_MAXIMUM))
		{
			bMatch = IMG_FALSE;
		}

		if (IsMove(psInst, &psDst, &psSrc))
		{
			bMatch = IMG_FALSE;
		}

		if (bMatch)
		{
			PUSE_REGISTER psReg = psDst;
			PUSE_REGISTER psTmpRepl = NULL;
			IMG_UINT32 uNode;

			/*
			  Got a replacement,
			  clobber the replacement binding
			  make the register a replacment for its replacement.

			  this does:
			  inst reg, ...
			  mov repl, reg
			  -->
			  inst repl, ...
			  mov repl, reg
			*/
			if (IsNode(psState, &sReplReg))
			{
				uNode = RegToNode(psState, &sReplReg);
				psTmpRepl = &(psState->asNodeRepl[uNode].sReg);

				if (EqualRegs(IMG_TRUE, psReg, psTmpRepl))
				{
					/* Clobber the binding */
					psTmpRepl->uType = USE_REGTYPE_UNDEF;
				}
			}
			/* Set the replacement for the destination */
			SetRegRepl(psState, psReg, &sReplReg);

			/* Replace the destination of inst with the source of the mov */
			psDst->uType = sReplReg.uType;
			psDst->uNumber = sReplReg.uNumber;

			/* Record the change */
			bChange = IMG_TRUE;
		}
	}
	else
	{
		IMG_BOOL bMovChange = IMG_FALSE;
		PUSE_INST psTmp;

		/* Move elimination */
		bMovChange = MunchMovs(psState, psBlock, psInst, NULL,
							   NULL, NULL, &psTmp);
		if (bMovChange)
		{
			bChange = IMG_TRUE;
			psNextInst = psTmp;
		}
	}

  Exit:
	if (ppsNextInst != NULL)
		(*ppsNextInst) = psNextInst;
	return bChange;
}

/*****************************************************************************
 FUNCTION	: EliminateMovesBlock

 PURPOSE	: Try to remove mov instructions from a block

 PARAMETERS	: psState     - Optimiser state
              psBlock     - First instruction in block
              psLimit     - End of block
              bFinal      - Whether this is the final block

 OUTPUT     : psNewBlock  - New instruction block
              ppsEndInst  - Instruction at end of processed block

 RETURNS	: Whether there was any change
*****************************************************************************/
static
IMG_BOOL EliminateMovesBlock(PUSEOPT_STATE psState,
							 PUSE_BLOCK psBlock,
							 IMG_BOOL bFinal,
							 PUSE_INST* ppsEndInst)
{
	IMG_BOOL bChanged = IMG_FALSE;
	PUSE_INST psEndInst = NULL;
	PUSE_INST psNext = NULL;
	PUSE_INST psCurr = NULL;
	IMG_BOOL bEarlyEnd;

    psNext = NULL;
	bEarlyEnd = IMG_FALSE;
    for (psCurr = psBlock->psFirst;
         psCurr != NULL;
         psCurr = psNext)
    {
		IMG_BOOL bMovChange;
		PUSE_INST psNextInst = NULL;

        psNext = GetNextInst(psCurr);

        /* Test for instructions which aren't handled */
		if (IsLabel(psCurr))
		{
			continue;
		}

        if (IsBranch(psCurr))
        {
			bEarlyEnd = IMG_TRUE;
            break;
        }

        /* Eliminate moves */
		bMovChange = EliminateMoves(psState, psBlock, psCurr, &psNextInst);
		if (bMovChange)
		{
			psNext = psNextInst;
		}
    }
	/* Record the instruction ending the processed block */
	psEndInst = psCurr;

	/* Dump the register values */
	if (bChanged)
	{
		/* Test whether iteration reached the specified end of block */
		if (bEarlyEnd)
		{
			/* Unexpected end so this can't be the final block */
			bFinal = IMG_FALSE;
		}

		if (IsBlockEnd(psCurr))
		{
            /* Dump the current register assignments */
			SetRegValues(psState, psBlock, psCurr, bFinal);
		}
    }

	if (ppsEndInst != NULL)
		(*ppsEndInst) = psEndInst;

	return bChanged;
}

/*****************************************************************************
 FUNCTION	: FindBranchTarget

 PURPOSE	: Get the instruction which is the target of a (forward) branch

 PARAMETERS	: psInstList    - Start of list to search
              uLabel        - Label of the target

 RETURNS	: The labeled instruction targetd by the branch or NULL
*****************************************************************************/
static
PUSE_INST FindBranchTarget(PUSE_INST psInstList, IMG_UINT32 uLabel)
{
	PUSE_INST psCurr;

	for (psCurr = psInstList;
		 psCurr != NULL;
		 psCurr = GetNextInst(psCurr))
	{
		PUSE_REGISTER psReg = &(psCurr->asArg[0]);

		if (psCurr->uOpcode == USEASM_OP_LABEL &&
			psReg->uType == USEASM_REGTYPE_LABEL &&
			psReg->uNumber == uLabel)
		{
			/* Found */
			return psCurr;
		}
	}
	/* Not found */
	return NULL;
}

/*****************************************************************************
 FUNCTION	: NewBlock

 PURPOSE	: Create a new block

 PARAMETERS	: psState    - Optimiser state
              psStart    - First instruction
              psLast     - Last instruction
              psPrev     - Previous block
              psNext     - Next block

 RETURNS	: Nothing
*****************************************************************************/
static
PUSE_BLOCK NewBlock(PUSEOPT_STATE psState,
					PUSE_INST psStart,
					PUSE_INST psEnd,
					PUSE_BLOCK psPrev,
					PUSE_BLOCK psNext)
{
	PUSE_BLOCK psTmp;

	psTmp = UseoptAlloc(psState, sizeof(USE_BLOCK));
	if (psTmp == NULL)
	{
		return NULL;
	}
	UseAsm_MemSet(psTmp, 0, sizeof(*psTmp));

	psTmp->psFirst = psStart;
	psTmp->psLast = psEnd;
	psTmp->psPrev = psPrev;
	psTmp->psNext = psNext;

	return psTmp;
}

/*****************************************************************************
 FUNCTION	: SplitBlock

 PURPOSE	: Split a block, creating a new block from a given instruction

 PARAMETERS	: psState    - Optimiser state
              psBlock    - Code block to split
              psInst     - Instruction to split at (becomes the first instruction
                           of the new block).

 NOTES      : Assumes that psInst is in the block. Does nothing if psInst is NULL.

 RETURNS	: The new block
*****************************************************************************/
static
PUSE_BLOCK SplitBlock(PUSEOPT_STATE psState,
					  PUSE_BLOCK psBlock,
					  PUSE_INST psInst)
{
	PUSE_BLOCK psNewBlock = NULL;
	PUSE_INST psInstPrev;

	if (psInst == NULL)
		return NULL;
	psInstPrev = psInst->psPrev;

	/* Make the new block */
	psNewBlock = NewBlock(psState, psInst, psBlock->psLast,
						  psBlock, psBlock->psNext);

	/* Reset the old block */
	if (psBlock->psFirst == psInst)
		psBlock->psFirst = NULL;
	psBlock->psLast = psInstPrev;

	/* Attach the new block */
	if (psNewBlock->psNext != NULL)
		psNewBlock->psNext->psPrev = psNewBlock;
	psBlock->psNext = psNewBlock;

    /* Cut the existing list */
	CutList(psInst);

	/* Done */
	return psNewBlock;
}

/*****************************************************************************
 FUNCTION	: JoinBlocks

 PURPOSE	: Combine two blocks.

 PARAMETERS	: psState    - Optimiser state
              psFirst    - First code block
              psSecond   - Second code block

 NOTES      : Deletes one of the code blocks.

 RETURNS	: The new block
*****************************************************************************/
static
PUSE_BLOCK JoinBlock(PUSEOPT_STATE psState,
					 PUSE_BLOCK psFirstBlock,
					 PUSE_BLOCK psSecondBlock)
{
	PUSE_BLOCK psNewBlock = NULL;


	if (psFirstBlock == NULL)
		return psSecondBlock;
	if (psSecondBlock == NULL)
		return psFirstBlock;

	psNewBlock = psFirstBlock;

    if (psFirstBlock->psFirst == NULL)
    {
        psNewBlock->psFirst = psSecondBlock->psFirst;
    }
    else
    {
        AppendList(psState, psFirstBlock->psLast, psSecondBlock->psFirst);
        psNewBlock->psFirst = psFirstBlock->psFirst;
        psNewBlock->psLast = psSecondBlock->psLast;
    }

	psNewBlock->psPrev = psFirstBlock->psPrev;
	if (psNewBlock->psPrev != NULL)
		psNewBlock->psPrev->psNext = psNewBlock;

	psNewBlock->psNext = psSecondBlock->psNext;
	if (psNewBlock->psNext != NULL)
		psNewBlock->psNext->psPrev = psNewBlock;

	/* Delete the second block */
	UseoptFree(psState, psSecondBlock);

	/* Done */
	return psNewBlock;
}

/*****************************************************************************
 FUNCTION	: FlattenBlocks

 PURPOSE	: Flatten a list of blocks into a list of instructions

 PARAMETERS	: psState    - Optimiser state
              psFirst    - First block

 NOTES      : Deletes the empty code blocks

 RETURNS	: The start of the instruction list
*****************************************************************************/
static
PUSE_BLOCK FlattenBlocks(PUSEOPT_STATE psState,
						 PUSE_BLOCK psBlock)
{
	PUSE_BLOCK psRet;
	PUSE_BLOCK psCurr, psNext = NULL;

	if (psBlock == NULL)
		return NULL;

	psRet = psBlock;
	psNext = NULL;
	for (psCurr = psRet->psNext;
		 psCurr != NULL;
		 psCurr = psNext)
	{
		psNext = psCurr->psNext;

		psRet = JoinBlock(psState, psRet, psCurr);
	}

	return psRet;
}

#if defined(USEOPT_DCE)
/*****************************************************************************
 FUNCTION	: IsDualIssue

 PURPOSE	: Test whether an instruction is dual-issued

 PARAMETERS	: psInst     - Instruction to test

 RETURNS	: IMG_TRUE iff psInst is a dual-issue instruction
*****************************************************************************/
static
IMG_BOOL IsDualIssue(PUSE_INST psInst)
{
	if (!IsMultiPart(psInst))
		return IMG_FALSE;

	/* Scalar dual-issue */
	if (psInst->uOpcode == USEASM_OP_FMAD ||
		psInst->uOpcode == USEASM_OP_MOV ||
		psInst->uOpcode == USEASM_OP_FADM ||
		psInst->uOpcode == USEASM_OP_FMSA ||
		psInst->uOpcode == USEASM_OP_FDDP ||
		psInst->uOpcode == USEASM_OP_FMINMAX ||
		psInst->uOpcode == USEASM_OP_IMA32 ||
		psInst->uOpcode == USEASM_OP_FSSQ)
	{
		return IMG_TRUE;
	}

	/* Vector dual-issue */
	if 			(
		psInst->uOpcode == USEASM_OP_VMAD3 ||
		psInst->uOpcode == USEASM_OP_VMAD4 ||
		psInst->uOpcode == USEASM_OP_VDP3 ||
		psInst->uOpcode == USEASM_OP_VDP4 ||
		psInst->uOpcode == USEASM_OP_VMUL3 ||
		psInst->uOpcode == USEASM_OP_VMUL4 ||
		psInst->uOpcode == USEASM_OP_VADD3 ||
		psInst->uOpcode == USEASM_OP_VADD4 ||
		psInst->uOpcode == USEASM_OP_VSSQ3 ||
		psInst->uOpcode == USEASM_OP_VSSQ4 ||
		psInst->uOpcode == USEASM_OP_VMOV3 ||
		psInst->uOpcode == USEASM_OP_VMOV4 ||
		psInst->uOpcode == USEASM_OP_FRSQ ||
		psInst->uOpcode == USEASM_OP_FRCP ||
		psInst->uOpcode == USEASM_OP_FMAD ||
		psInst->uOpcode == USEASM_OP_FADD ||
		psInst->uOpcode == USEASM_OP_FMUL ||
		psInst->uOpcode == USEASM_OP_FFRC ||
		psInst->uOpcode == USEASM_OP_FEXP ||
		psInst->uOpcode == USEASM_OP_FLOG)
	{
		return IMG_TRUE;
	}

	return IMG_FALSE;
}

/*****************************************************************************
 FUNCTION	: InvalidDCEInst

 PURPOSE	: Test whether for instruction not handled in dce

 PARAMETERS	: psInst    - Instruction to test

 RETURNS	: IMG_TRUE iff psInst can't be handled in dce.
*****************************************************************************/
static
IMG_BOOL InvalidDCEInst(PUSE_INST psInst)
{
	if (IsRepeating(psInst))
		return IMG_TRUE;

	switch (psInst->uOpcode)
	{
		case USEASM_OP_SMP1D:
		case USEASM_OP_SMP2D:
		case USEASM_OP_SMP3D:
		case USEASM_OP_SMP1DBIAS:
		case USEASM_OP_SMP2DBIAS:
		case USEASM_OP_SMP3DBIAS:
		case USEASM_OP_SMP1DREPLACE:
		case USEASM_OP_SMP2DREPLACE:
		case USEASM_OP_SMP3DREPLACE:
		case USEASM_OP_SMP1DGRAD:
		case USEASM_OP_SMP2DGRAD:
		case USEASM_OP_SMP3DGRAD:
			return IMG_TRUE;
		default:
			return IMG_FALSE;
	}
}

/*****************************************************************************
 FUNCTION	 : DCECalcInstDest

 PURPOSE	 : Establish whether an instruction writes to a live variable

 PARAMETERS	 : psState    - Optimiser state
               psInst     - Instruction to test

 INPUT/OUTPUT:
               auLiveVars - Bitset representing variables read/written

 RETURNS	 : TRUE iff instruction writes a live variable
*****************************************************************************/
static
IMG_BOOL DCECalcInstDest(PUSEOPT_STATE psState,
					  PUSE_INST psInst,
					  IMG_PUINT32 auLiveVars)
{
	IMG_UINT32 uIdx, uNumDests;
	PUSE_REGISTER psReg;
	IMG_UINT32 uNode;
	IMG_BOOL bDeadInstruction = IMG_TRUE;

	/* Get number of destinations */
	if (IsTest(psInst))
		uNumDests = 2;
	else
		uNumDests = 1;

	if (UseGetOpcodeArgumentCount(psInst->uOpcode) < uNumDests)
		uNumDests = UseGetOpcodeArgumentCount(psInst->uOpcode);

	for (uIdx = 0; uIdx < uNumDests; ++uIdx)
	{
		psReg = &(psInst->asArg[uIdx]);

		/* Test whether this instruction writes to a live register */
		if (!IsNode(psState, psReg))
		{
			bDeadInstruction = IMG_FALSE;
			continue;
		}
		uNode = RegToNode(psState, psReg);

		if (UseoptGetBit(auLiveVars, uNode))
		{
			/*
			  Register is read after being written here, so
			  instruction is not dead.
			*/
			bDeadInstruction = IMG_FALSE;
		}

		/* Kill the destination register */
		UseoptSetBit(auLiveVars, uNode, IMG_FALSE);
	}

	if (bDeadInstruction)
		return IMG_FALSE;
	else
		return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	 : DCECalcInstSrc

 PURPOSE	 : Record which variables are read by an instruction

 PARAMETERS	 : psState    - Optimiser state
               psInst     - Instruction to test
               bFullInst  - Whether instruction has any destinations

 OUTPUT:       auLiveVars - Bitset representing variables read/written

 RETURNS	 : Nothing
*****************************************************************************/
static
IMG_VOID DCECalcInstSrc(PUSEOPT_STATE psState,
						PUSE_INST psInst,
						IMG_BOOL bHasDest,
						IMG_PUINT32 auLiveVars)
{
	IMG_UINT32 uIdx, uFirstSrc, uNumOperands, uNumDests;
	PUSE_REGISTER psReg;
	IMG_UINT32 uNode;

	/* Get number of destinations */
	if (bHasDest)
	{
		if (IsTest(psInst))
			uNumDests = 2;
		else
			uNumDests = 1;
	}
	else
	{
		uNumDests = 0;
	}
	if (UseGetOpcodeArgumentCount(psInst->uOpcode) < uNumDests)
		uNumDests = UseGetOpcodeArgumentCount(psInst->uOpcode);

	/* Get number of Operands */
	uFirstSrc = uNumDests;
	uNumOperands = UseGetOpcodeArgumentCount(psInst->uOpcode);

	for (uIdx = uFirstSrc; uIdx < uNumOperands; ++uIdx)
	{
		psReg = &(psInst->asArg[uIdx]);

		/* Test whether this instruction writes to a live register */
		if (!IsNode(psState, psReg))
		{
			continue;
		}
		uNode = RegToNode(psState, psReg);

		/* Record the register use */
		UseoptSetBit(auLiveVars, uNode, IMG_TRUE);
	}
}

/*****************************************************************************
 FUNCTION	 : DCEBlock

 PURPOSE	 : Apply dead-code elimination to a single block

 PARAMETERS	 : psState    - Optimiser state
               psBlock    - Block to which dce should be applied

 INPUT/OUTPUT:
               auLiveVars - Bitset representing variables live out of the block

 RETURNS	 : TRUE iff dce processed the entire block

 NOTES       : Does very simple, coarse-grain (register level) dead code elimination.
               Should be adequate for programs which do not include flow-control
*****************************************************************************/
static
IMG_BOOL DCEBlock(PUSEOPT_STATE psState,
				  PUSE_BLOCK psBlock,
				  IMG_PUINT32 auLiveVars)
{
	PUSE_INST psInst, psPrev;

	if (psBlock == NULL)
		return IMG_TRUE;

	/* Iterate backwards through the block */
	psPrev = NULL;
	for (psInst = psBlock->psLast;
		 psInst != NULL;
		 psInst = psPrev)
	{
		IMG_BOOL bDeadInstruction = IMG_TRUE;
		IMG_BOOL bMultiPart = IMG_FALSE;
		IMG_BOOL bDualIssue = IMG_FALSE;
		psPrev = psInst->psPrev;

		/* Test for multi-part instruction */
		if (psInst->uFlags2 & USEASM_OPFLAGS2_COISSUE)
		{
			bMultiPart = IMG_TRUE;
		}
		else if (psInst->psPrev != NULL)
		{
			if (IsMultiPart(psInst->psPrev))
			{
				bMultiPart = IMG_TRUE;
			}
		}
		if (bMultiPart)
		{
			/* Advance the  instruction pointer */
			if (psPrev == NULL)
			{
				/* Dual-issue must have a second instruction */
				UseoptAbort(psState, USEOPT_INVALID_PROGRAM);
			}
			psInst = psPrev;
			psPrev = psInst->psPrev;
		}

		/* Test for invalid instruction */
		if (InvalidDCEInst(psInst))
			return IMG_FALSE;

		/* Analyse destination of instruction */
		if (DCECalcInstDest(psState, psInst, auLiveVars))
			bDeadInstruction = IMG_FALSE;

		/* Test for dual issue */
		if (HasDualIssue(psState->psTarget) &&
			IsDualIssue(psInst))
		{
			bDualIssue = IMG_TRUE;
			/* Analyse the co-instruction */
			if (DCECalcInstDest(psState, psInst->psNext, auLiveVars))
			{
				bDeadInstruction = IMG_FALSE;
			}
		}

		/* Remove the instruction if it is dead */
		if (bDeadInstruction)
		{
			/* Remove and delete the instruction */
			if (psPrev != NULL)
			{
				IMG_UINT32 uFlags = 0;

				uFlags = GetInstFlags(psInst);
				SetInstFlags(psPrev, uFlags & (USE_PROGEND | USE_SKIPINV), IMG_TRUE);
			}
			DeleteInst(psState, psBlock, psInst);

			/* Go on to the next instruction */
			continue;
		}

		/* Analyses sources to record which variables are read */
		DCECalcInstSrc(psState, psInst, IMG_TRUE, auLiveVars);
		if (bMultiPart)
		{
			DCECalcInstSrc(psState, psInst->psNext, bDualIssue, auLiveVars);
		}
	}

	return IMG_TRUE;
}
#endif /* defined(USEOPT_DCE) */

/*****************************************************************************
 FUNCTION	 : FormRepeatsBlock

 PURPOSE	 : Form repeat groups in a block

 PARAMETERS	 : psState    - Optimiser state
               psBlock    - Block to operate on

 RETURNS	 : TRUE iff the entire block was processed.
               FALSE if processing stopped for any reason.

 NOTES       : Assumes that MOE state is set to default
               (increment by 1 on all operands).
*****************************************************************************/
static
IMG_BOOL FormRepeatsBlock(PUSEOPT_STATE psState,
						  PUSE_BLOCK psBlock)
{
	PUSE_INST psInst, psNext;
	const IMG_UINT32 uMaxRepeatCount = EURASIA_USE_MAXIMUM_REPEAT;
	const IMG_UINT32 uNumMOEOperands = 4;

	if (psBlock == NULL)
		return IMG_TRUE;

	/* Iterate forwards through the block */
	psNext = NULL;
	for (psInst = psBlock->psFirst;
		 psInst != NULL;
		 psInst = psNext)
	{
		PUSE_INST psNextCandidate;
		IMG_UINT32 uRepeatCount;
		UseasmOpcode uOpcode;
		IMG_BOOL bFormGroup = IMG_FALSE;
		IMG_UINT32 uNumOperands;

		psNext = GetNextInst(psInst);
		if (IsMultiPart(psInst))
		{
			/* Can't repeat a mult-part instruction */
			continue;
		}

		/* Test for invalid instructions */
		if (IsMOEControl(psInst))
		{
			/* Can't go any further */
			return IMG_FALSE;
		}

		/* Test for repeatable instructions */
		if ((!(OpcodeDescFlags(psInst->uOpcode) & USE_DESCFLAG_REPEAT)) ||
			IsRepeating(psInst) ||
			IsTest(psInst))
		{
			/* Instruction doesn't repeat */
			continue;
		}

		/* Try to form a repeat group */
		uOpcode = psInst->uOpcode;
		uRepeatCount = 1;
		bFormGroup = IMG_FALSE;
		psNextCandidate = NULL;
		for ( ;
			  (psNext != NULL) && (psNext->uOpcode == uOpcode);
			  psNext = psNextCandidate)
		{
			IMG_UINT32 uIdx = 0;
			IMG_BOOL bAccept;

			psNextCandidate = GetNextInst(psNext);

			/* Test the candidate */
			if (IsRepeating(psNext) ||
				IsTest(psNext))
			{
				/* Done */
				break;
			}
			/* Test the repeat limit */
			if ((uRepeatCount + 1) >= uMaxRepeatCount)
			{
				/* Done */
				break;
			}

			/* Test the operands */
			uNumOperands = uNumMOEOperands;
			if (uNumOperands > UseGetOpcodeArgumentCount(uOpcode))
			{
				uNumOperands = UseGetOpcodeArgumentCount(uOpcode);
			}
			bAccept = IMG_TRUE;
			for(uIdx = 0; uIdx < uNumOperands; ++uIdx)
			{
				PUSE_REGISTER psReg = &psInst->asArg[uIdx];
				PUSE_REGISTER psOther = &psNext->asArg[uIdx];

				/* Test register types and settings */
				if (psReg->uType != psOther->uType &&
					psReg->uIndex != psOther->uIndex &&
					psReg->uFlags != psOther->uFlags)
				{
					bAccept = IMG_FALSE;
					break;
				}
				/* Test register numbers */
				if ((psReg->uNumber + uRepeatCount) != psOther->uNumber)
				{
					bAccept = IMG_FALSE;
					break;
				}
			}
			/* Test whether operands match */
			if (!bAccept)
			{
				/* Stop the group at this instruction */
				break;
			}
			else
			{
				/* Accept the instruction into the group. */
				/* Increment the repeat count */
				uRepeatCount += 1;
				/* Remove the candidate from the code block */
				DeleteInst(psState, psBlock, psNext);

				/* Indicate that a group can be formed */
				bFormGroup = IMG_TRUE;
			}
		}
		/* If a group can be formed, set up the lead instruction */
		if (bFormGroup)
		{
			SetRepeatCount(psInst, uRepeatCount);
		}
	}

	return IMG_TRUE;
}

/*****************************************************************************
 FUNCTION	: OptimiseBlock

 PURPOSE	: Apply optimisations to a block of code

 PARAMETERS	: psState    - Optimiser state
              psProgram  - Program to optimise
              psStart    - Starting point

 RETURNS	: The optimised block
*****************************************************************************/
static
PUSE_INST OptimiseBlock(PUSEOPT_STATE psState,
						PUSE_INST psProgram,
						PUSE_INST psStart)
{
	PUSE_INST psOptProgram = psProgram;
	PUSE_INST psProgEnd;
	PUSE_INST psCurr;
	IMG_BOOL bEarlyExit;
	PUSE_BLOCK psMainBlock;
	PUSE_BLOCK psCodeBlock, psNextBlock;
	PUSE_INST psEndInst = NULL;
	IMG_BOOL bHasControlFlow = IMG_FALSE;

	if (psState == NULL)
	{
		return psProgram;
	}

	/* Initialise the block stack with the main program */
	psProgEnd = GetLastInst(psProgram);

	psMainBlock = NewBlock(psState,
						   psStart, psProgEnd,
						   NULL, NULL);
	if (psMainBlock == NULL)
		return psProgram;

	/*
	  Optimise each block in the state
	 */
	bEarlyExit = IMG_FALSE;

	psNextBlock = NULL;
	for (psCodeBlock = psMainBlock;
		 psCodeBlock != NULL;
		 psCodeBlock = psNextBlock)
	{
		psNextBlock = psCodeBlock->psNext;
		psCurr = psCodeBlock->psFirst;

		/*
		  Start of a code block: Try to eliminate moves and find the
		  first branch instruction.
		*/
		{
			INST_LIST sNewBlock = INST_LIST_DEFAULT;
			IMG_BOOL bFinalBlock = IMG_FALSE;

			/* Remove moves from the block up to the first branch. */

			psEndInst = NULL;
			UseAsm_MemSet(&sNewBlock, 0, sizeof(sNewBlock));
			EliminateMovesBlock(psState,
								psCodeBlock,
								bFinalBlock,
								&psEndInst);
		}

		/* Test how the pass through the block ended */
		if (psEndInst != NULL &&
			IsBranch(psEndInst))
		{
			IMG_UINT32 uLabel;
			PUSE_INST psTarget = NULL;
			PUSE_BLOCK psFollowBlock = NULL;

			/* Got a branch, start new code blocks */
			bHasControlFlow = IMG_FALSE;
			psCurr = psEndInst;

			/* Look ahead for the branch target */
			uLabel = GetBranchLabel(psCurr);
			psTarget = FindBranchTarget(psCurr, uLabel);

			/* Unknown control flow */
			if (psTarget == NULL)
			{
				bEarlyExit = IMG_TRUE;
				break;
			}

			/* Split off the block falling the branch */
			psFollowBlock = SplitBlock(psState, psCodeBlock, GetNextInst(psEndInst));

			/* Split off the block from the target */
			if (GetNextInst(psEndInst) != psTarget)
			{
				SplitBlock(psState, psFollowBlock, psTarget);
			}

			/* Set the next code block to process */
			psNextBlock = psFollowBlock;
		}

		/* Apply peephole optimisation to current block */
		PeepHoleBlock(psState, psCodeBlock);

		/* Dump the current register assignments */
		if (psNextBlock != NULL)
		{
			SetRegValues(psState, psCodeBlock, psCodeBlock->psLast, IMG_FALSE);
		}
	}

	/* Done optimising, flatten the code blocks */
	psMainBlock = FlattenBlocks(psState, psMainBlock);

#if defined(USEOPT_DCE)
	/* Apply dead-code elimination */
	if (!bHasControlFlow)
	{
		IMG_PUINT32 auLiveVars = NULL;
		IMG_UINT32 uIdx;
		const IMG_UINT32 uNumOutputs = psState->psOptData->uNumOutRegs;
		const IMG_UINT32 uBitsetSize = (UINTS_TO_SPAN_BITS(psState->uNumNodes) *
										sizeof(IMG_UINT32));

		auLiveVars = UseoptAlloc(psState, uBitsetSize);
		UseAsm_MemSet(auLiveVars, 0, uBitsetSize);

		for (uIdx = 0; uIdx < uNumOutputs; ++ uIdx)
		{
			PUSE_REGISTER psReg = &psState->psOptData->asOutRegs[uIdx];
			IMG_UINT32 uNode = 0;

			if (IsNode(psState, psReg))
			{
				uNode = RegToNode(psState, psReg);
				UseoptSetBit(auLiveVars, uNode, IMG_TRUE);
			}
		}
		DCEBlock(psState, psMainBlock, auLiveVars);
		UseoptFree(psState, auLiveVars);
	}
#endif /* defined(USEOPT_DCE) */

	/* Form instruction repeat groups */
	if (!bHasControlFlow)
	{
		FormRepeatsBlock(psState, psMainBlock);
	}

	/* Test whether any instructions are left */
	if (psMainBlock->psFirst == NULL)
	{
		ENSURE(psState, psMainBlock->psLast == NULL);
		UseoptFree(psState, psMainBlock);
		psMainBlock = NULL;
	}

	/* Set the output register values */
	if (psMainBlock != NULL)
	{
		PUSE_INST psLastInst = NULL;
		IMG_BOOL bIsLast = IMG_FALSE;

		psLastInst = GetInstStart(psMainBlock->psLast);
		if (IsProgEnd(psLastInst))
		{
			bIsLast = IMG_TRUE;
			SetProgEnd(psLastInst, IMG_FALSE);
		}

		if (bEarlyExit)
		{
			SetRegValues(psState, psMainBlock, psEndInst, IMG_FALSE);
		}
		else if (psLastInst != NULL)
		{
			if (IsBranch(psLastInst))
			{
				/* Insert before the last instruction in the block */
				SetRegValues(psState, psMainBlock, psLastInst, IMG_TRUE);
			}
			else
			{
				/* Insert after the last instruction in the block */
				SetRegValues(psState, psMainBlock, NULL, IMG_TRUE);
			}
		}
		psLastInst = GetInstStart(psMainBlock->psLast);
		if (bIsLast)
		{
			SetProgEnd(psLastInst, IMG_TRUE);
		}
	}

	/* Extract the optimised program */
	if (psMainBlock != NULL)
	{
		psOptProgram = psMainBlock->psFirst;
	}
	else
	{
		psOptProgram = NULL;
	}


	/* Exit */
	UseoptFree(psState, psMainBlock);

	return psOptProgram;
}

/*****************************************************************************
 FUNCTION	: UseoptProgram

 PURPOSE	: Optimse a USE program

 PARAMETERS	: psTarget    - Target to assemble for
			  psProgram   - First instruction in list
			  uCodeOffset - Offset of code in memory
			  psContext   - Use assembler context

 RETURNS	: Optimised program
*****************************************************************************/
IMG_INTERNAL IMG_VOID
IMG_CALLCONV UseoptProgram(PSGX_CORE_INFO psTarget,
						   PUSEASM_CONTEXT psContext,
						   PUSEOPT_DATA psUseoptData)
{
	PUSE_INST psOptProg = NULL;
	PUSEOPT_STATE psState = NULL;

	PVR_UNREFERENCED_PARAMETER(psState);

    if (psUseoptData->psProgram == NULL)
        return;

    /* Initialise the state */
	FixUseoptData(psUseoptData);

	psState = InitOptState(psTarget, psContext, psUseoptData);
	if (psState == NULL)
	{
		psUseoptData->eStatus = USEOPT_MEMALLOC;
		return;
	}

	/* Set the error handler and optimise the code */
	if (setjmp(psState->sErrorHandler) == 0)
	{
		PUSE_INST psStart;

		psState->bErrorHandlerValid = IMG_TRUE;

		if (psUseoptData->psStart == NULL)
			psStart = psUseoptData->psProgram;
		else
			psStart = psUseoptData->psStart;

		/* Optimise the code */
		if (psState != NULL)
		{
			psOptProg = OptimiseBlock(psState, psUseoptData->psProgram, psStart);
		}
	}

	/* Set the exit status */
	psUseoptData->eStatus = psState->eStatus;

	/* Free allocated memory */
	FreeOptState(psState);

	/* return the optimised code */
    psUseoptData->psProgram = psOptProg;
}

/*****************************************************************************
 FUNCTION	: UseoptAssembler

 PURPOSE	: Optimse and assemble a USE program

 PARAMETERS	: psTarget    - Target to assemble for
			  psProgram   - First instruction in list
			  uCodeOffset - Offset of code in memory
			  psContext   - Use assembler context

 RETURNS	: The number of instructions on success or -1 on failure.
*****************************************************************************/
IMG_INTERNAL IMG_UINT32
IMG_CALLCONV UseoptAssembler(PSGX_CORE_INFO psTarget,
                             IMG_PUINT32 puInst,
							 IMG_UINT32 uCodeOffset,
							 PUSEASM_CONTEXT psContext,
							 PUSEOPT_DATA psUseoptData)
{
    UseoptProgram(psTarget,
                  psContext,
                  psUseoptData);

	/* Assemble the optimised code */
	return UseAssembler(UseAsmGetCoreDesc(psTarget), psUseoptData->psProgram, puInst, uCodeOffset, psContext);
}

/******************************************************************************
 End of file (useopt.c)
******************************************************************************/
